1#![cfg_attr(not(feature = "program"), allow(unused))]
2use crate::error::DexError;
3use crate::matching::{OrderType, Side};
4use bytemuck::cast;
5use serde::{Deserialize, Serialize};
6use solana_program::{
7 instruction::{AccountMeta, Instruction},
8 pubkey::Pubkey,
9 sysvar::rent,
10};
11use std::convert::TryInto;
12
13use arrayref::{array_ref, array_refs};
14use num_enum::{IntoPrimitive, TryFromPrimitive};
15use std::num::NonZeroU64;
16
17#[cfg(test)]
18use proptest::prelude::*;
19#[cfg(test)]
20use proptest_derive::Arbitrary;
21
22pub mod srm_token {
23 use solana_program::declare_id;
24 declare_id!("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt");
25}
26
27pub mod msrm_token {
28 use solana_program::declare_id;
29 declare_id!("MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L");
30}
31
32pub mod disable_authority {
33 use solana_program::declare_id;
34 declare_id!("5ZVJgwWxMsqXxRMYHXqMwH2hd4myX5Ef4Au2iUsuNQ7V");
35}
36
37pub mod fee_sweeper {
38 use solana_program::declare_id;
39 declare_id!("DeqYsmBd9BnrbgUwQjVH4sQWK71dEgE6eoZFw3Rp4ftE");
40}
41
42#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
43#[cfg_attr(test, derive(Arbitrary))]
44#[cfg_attr(test, proptest(no_params))]
45#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
46pub struct InitializeMarketInstruction {
47 pub coin_lot_size: u64,
56 pub pc_lot_size: u64,
57 pub fee_rate_bps: u16,
58 pub vault_signer_nonce: u64,
59 pub pc_dust_threshold: u64,
60}
61
62#[derive(
63 PartialEq, Eq, Copy, Clone, Debug, TryFromPrimitive, IntoPrimitive, Serialize, Deserialize,
64)]
65#[cfg_attr(test, derive(Arbitrary))]
66#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
67#[repr(u8)]
68pub enum SelfTradeBehavior {
69 DecrementTake = 0,
70 CancelProvide = 1,
71 AbortTransaction = 2,
72}
73
74#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
75#[cfg_attr(test, derive(Arbitrary))]
76pub struct SendTakeInstruction {
77 pub side: Side,
78
79 #[cfg_attr(
80 test,
81 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
82 )]
83 pub limit_price: NonZeroU64,
84
85 #[cfg_attr(
86 test,
87 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
88 )]
89 pub max_coin_qty: NonZeroU64,
90 #[cfg_attr(
91 test,
92 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
93 )]
94 pub max_native_pc_qty_including_fees: NonZeroU64,
95
96 pub min_coin_qty: u64,
97 pub min_native_pc_qty: u64,
98
99 pub limit: u16,
100}
101
102#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
103#[cfg_attr(test, derive(Arbitrary))]
104pub struct NewOrderInstructionV3 {
105 pub side: Side,
106
107 #[cfg_attr(
108 test,
109 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
110 )]
111 pub limit_price: NonZeroU64,
112
113 #[cfg_attr(
114 test,
115 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
116 )]
117 pub max_coin_qty: NonZeroU64,
118 #[cfg_attr(
119 test,
120 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
121 )]
122 pub max_native_pc_qty_including_fees: NonZeroU64,
123
124 pub self_trade_behavior: SelfTradeBehavior,
125
126 pub order_type: OrderType,
127 pub client_order_id: u64,
128 pub limit: u16,
129}
130
131#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
132#[cfg_attr(test, derive(Arbitrary))]
133pub struct NewOrderInstructionV2 {
134 pub side: Side,
135 #[cfg_attr(
136 test,
137 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
138 )]
139 pub limit_price: NonZeroU64,
140 #[cfg_attr(
141 test,
142 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
143 )]
144 pub max_qty: NonZeroU64,
145 pub order_type: OrderType,
146 pub client_id: u64,
147 pub self_trade_behavior: SelfTradeBehavior,
148}
149
150#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
151#[cfg_attr(test, derive(Arbitrary))]
152pub struct NewOrderInstructionV1 {
153 pub side: Side,
154 #[cfg_attr(
155 test,
156 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
157 )]
158 pub limit_price: NonZeroU64,
159 #[cfg_attr(
160 test,
161 proptest(strategy = "(1u64..=std::u64::MAX).prop_map(|x| NonZeroU64::new(x).unwrap())")
162 )]
163 pub max_qty: NonZeroU64,
164 pub order_type: OrderType,
165 pub client_id: u64,
166}
167
168impl NewOrderInstructionV1 {
169 pub fn add_self_trade_behavior(
170 self,
171 self_trade_behavior: SelfTradeBehavior,
172 ) -> NewOrderInstructionV2 {
173 let NewOrderInstructionV1 {
174 side,
175 limit_price,
176 max_qty,
177 order_type,
178 client_id,
179 } = self;
180 NewOrderInstructionV2 {
181 side,
182 limit_price,
183 max_qty,
184 order_type,
185 client_id,
186 self_trade_behavior,
187 }
188 }
189}
190
191impl SendTakeInstruction {
192 fn unpack(data: &[u8; 46]) -> Option<Self> {
193 let (
194 &side_arr,
195 &price_arr,
196 &max_coin_qty_arr,
197 &max_native_pc_qty_arr,
198 &min_coin_qty_arr,
199 &min_native_pc_qty_arr,
200 &limit_arr,
201 ) = array_refs![data, 4, 8, 8, 8, 8, 8, 2];
202
203 let side = Side::try_from_primitive(u32::from_le_bytes(side_arr).try_into().ok()?).ok()?;
204 let limit_price = NonZeroU64::new(u64::from_le_bytes(price_arr))?;
205 let max_coin_qty = NonZeroU64::new(u64::from_le_bytes(max_coin_qty_arr))?;
206 let max_native_pc_qty_including_fees =
207 NonZeroU64::new(u64::from_le_bytes(max_native_pc_qty_arr))?;
208 let min_coin_qty = u64::from_le_bytes(min_coin_qty_arr);
209 let min_native_pc_qty = u64::from_le_bytes(min_native_pc_qty_arr);
210 let limit = u16::from_le_bytes(limit_arr);
211
212 Some(SendTakeInstruction {
213 side,
214 limit_price,
215 max_coin_qty,
216 max_native_pc_qty_including_fees,
217 min_coin_qty,
218 min_native_pc_qty,
219 limit,
220 })
221 }
222}
223
224impl NewOrderInstructionV3 {
225 fn unpack(data: &[u8; 46]) -> Option<Self> {
226 let (
227 &side_arr,
228 &price_arr,
229 &max_coin_qty_arr,
230 &max_native_pc_qty_arr,
231 &self_trade_behavior_arr,
232 &otype_arr,
233 &client_order_id_bytes,
234 &limit_arr,
235 ) = array_refs![data, 4, 8, 8, 8, 4, 4, 8, 2];
236
237 let side = Side::try_from_primitive(u32::from_le_bytes(side_arr).try_into().ok()?).ok()?;
238 let limit_price = NonZeroU64::new(u64::from_le_bytes(price_arr))?;
239 let max_coin_qty = NonZeroU64::new(u64::from_le_bytes(max_coin_qty_arr))?;
240 let max_native_pc_qty_including_fees =
241 NonZeroU64::new(u64::from_le_bytes(max_native_pc_qty_arr))?;
242 let self_trade_behavior = SelfTradeBehavior::try_from_primitive(
243 u32::from_le_bytes(self_trade_behavior_arr)
244 .try_into()
245 .ok()?,
246 )
247 .ok()?;
248 let order_type =
249 OrderType::try_from_primitive(u32::from_le_bytes(otype_arr).try_into().ok()?).ok()?;
250 let client_order_id = u64::from_le_bytes(client_order_id_bytes);
251 let limit = u16::from_le_bytes(limit_arr);
252
253 Some(NewOrderInstructionV3 {
254 side,
255 limit_price,
256 max_coin_qty,
257 max_native_pc_qty_including_fees,
258 self_trade_behavior,
259 order_type,
260 client_order_id,
261 limit,
262 })
263 }
264}
265
266impl NewOrderInstructionV1 {
267 fn unpack(data: &[u8; 32]) -> Option<Self> {
268 let (&side_arr, &price_arr, &max_qty_arr, &otype_arr, &client_id_bytes) =
269 array_refs![data, 4, 8, 8, 4, 8];
270 let client_id = u64::from_le_bytes(client_id_bytes);
271 let side = match u32::from_le_bytes(side_arr) {
272 0 => Side::Bid,
273 1 => Side::Ask,
274 _ => return None,
275 };
276 let limit_price = NonZeroU64::new(u64::from_le_bytes(price_arr))?;
277 let max_qty = NonZeroU64::new(u64::from_le_bytes(max_qty_arr))?;
278 let order_type = match u32::from_le_bytes(otype_arr) {
279 0 => OrderType::Limit,
280 1 => OrderType::ImmediateOrCancel,
281 2 => OrderType::PostOnly,
282 _ => return None,
283 };
284 Some(NewOrderInstructionV1 {
285 side,
286 limit_price,
287 max_qty,
288 order_type,
289 client_id,
290 })
291 }
292}
293#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
294#[cfg_attr(test, derive(Arbitrary))]
295#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
296pub struct CancelOrderInstructionV2 {
297 pub side: Side,
298 pub order_id: u128,
299}
300
301#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
302#[cfg_attr(test, derive(Arbitrary))]
303#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
304pub struct CancelOrderInstruction {
305 pub side: Side,
306 pub order_id: u128,
307 pub owner: [u64; 4], pub owner_slot: u8,
309}
310
311impl CancelOrderInstructionV2 {
312 fn unpack(data: &[u8; 20]) -> Option<Self> {
313 let (&side_arr, &oid_arr) = array_refs![data, 4, 16];
314 let side = Side::try_from_primitive(u32::from_le_bytes(side_arr).try_into().ok()?).ok()?;
315 let order_id = u128::from_le_bytes(oid_arr);
316 Some(CancelOrderInstructionV2 { side, order_id })
317 }
318}
319
320#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
321#[cfg_attr(test, derive(Arbitrary))]
322#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
323pub enum MarketInstruction {
324 InitializeMarket(InitializeMarketInstruction),
338 NewOrder(NewOrderInstructionV1),
349 MatchOrders(u16),
355 ConsumeEvents(u16),
361 CancelOrder(CancelOrderInstruction),
366 SettleFunds,
377 CancelOrderByClientId(u64),
382 DisableMarket,
385 SweepFees,
392 NewOrderV2(NewOrderInstructionV2),
403 NewOrderV3(NewOrderInstructionV3),
417 CancelOrderV2(CancelOrderInstructionV2),
424 CancelOrderByClientIdV2(u64),
431 SendTake(SendTakeInstruction),
444 CloseOpenOrders,
449 InitOpenOrders,
455 Prune(u16),
465 ConsumeEventsPermissioned(u16),
470}
471
472impl MarketInstruction {
473 pub fn pack(&self) -> Vec<u8> {
474 bincode::serialize(&(0u8, self)).unwrap()
475 }
476
477 pub fn unpack(versioned_bytes: &[u8]) -> Option<Self> {
478 if versioned_bytes.len() < 5 || versioned_bytes.len() > 58 {
479 return None;
480 }
481 let (&[version], &discrim, data) = array_refs![versioned_bytes, 1, 4; ..;];
482 if version != 0 {
483 return None;
484 }
485 let discrim = u32::from_le_bytes(discrim);
486 Some(match (discrim, data.len()) {
487 (0, 34) => MarketInstruction::InitializeMarket({
488 let data_array = array_ref![data, 0, 34];
489 let fields = array_refs![data_array, 8, 8, 2, 8, 8];
490 InitializeMarketInstruction {
491 coin_lot_size: u64::from_le_bytes(*fields.0),
492 pc_lot_size: u64::from_le_bytes(*fields.1),
493 fee_rate_bps: u16::from_le_bytes(*fields.2),
494 vault_signer_nonce: u64::from_le_bytes(*fields.3),
495 pc_dust_threshold: u64::from_le_bytes(*fields.4),
496 }
497 }),
498 (1, 32) => MarketInstruction::NewOrder({
499 let data_arr = array_ref![data, 0, 32];
500 NewOrderInstructionV1::unpack(data_arr)?
501 }),
502 (2, 2) => {
503 let limit = array_ref![data, 0, 2];
504 MarketInstruction::MatchOrders(u16::from_le_bytes(*limit))
505 }
506 (3, 2) => {
507 let limit = array_ref![data, 0, 2];
508 MarketInstruction::ConsumeEvents(u16::from_le_bytes(*limit))
509 }
510 (4, 53) => MarketInstruction::CancelOrder({
511 let data_array = array_ref![data, 0, 53];
512 let fields = array_refs![data_array, 4, 16, 32, 1];
513 let side = match u32::from_le_bytes(*fields.0) {
514 0 => Side::Bid,
515 1 => Side::Ask,
516 _ => return None,
517 };
518 let order_id = u128::from_le_bytes(*fields.1);
519 let owner = cast(*fields.2);
520 let &[owner_slot] = fields.3;
521 CancelOrderInstruction {
522 side,
523 order_id,
524 owner,
525 owner_slot,
526 }
527 }),
528 (5, 0) => MarketInstruction::SettleFunds,
529 (6, 8) => {
530 let client_id = array_ref![data, 0, 8];
531 MarketInstruction::CancelOrderByClientId(u64::from_le_bytes(*client_id))
532 }
533 (7, 0) => MarketInstruction::DisableMarket,
534 (8, 0) => MarketInstruction::SweepFees,
535 (9, 36) => MarketInstruction::NewOrderV2({
536 let data_arr = array_ref![data, 0, 36];
537 let (v1_data_arr, v2_data_arr) = array_refs![data_arr, 32, 4];
538 let v1_instr = NewOrderInstructionV1::unpack(v1_data_arr)?;
539 let self_trade_behavior = SelfTradeBehavior::try_from_primitive(
540 u32::from_le_bytes(*v2_data_arr).try_into().ok()?,
541 )
542 .ok()?;
543 v1_instr.add_self_trade_behavior(self_trade_behavior)
544 }),
545 (10, 46) => MarketInstruction::NewOrderV3({
546 let data_arr = array_ref![data, 0, 46];
547 NewOrderInstructionV3::unpack(data_arr)?
548 }),
549 (11, 20) => MarketInstruction::CancelOrderV2({
550 let data_arr = array_ref![data, 0, 20];
551 CancelOrderInstructionV2::unpack(data_arr)?
552 }),
553 (12, 8) => {
554 let client_id = array_ref![data, 0, 8];
555 MarketInstruction::CancelOrderByClientIdV2(u64::from_le_bytes(*client_id))
556 }
557 (13, 46) => MarketInstruction::SendTake({
558 let data_arr = array_ref![data, 0, 46];
559 SendTakeInstruction::unpack(data_arr)?
560 }),
561 (14, 0) => MarketInstruction::CloseOpenOrders,
562 (15, 0) => MarketInstruction::InitOpenOrders,
563 (16, 2) => {
564 let limit = array_ref![data, 0, 2];
565 MarketInstruction::Prune(u16::from_le_bytes(*limit))
566 }
567 (17, 2) => {
568 let limit = array_ref![data, 0, 2];
569 MarketInstruction::ConsumeEventsPermissioned(u16::from_le_bytes(*limit))
570 }
571 _ => return None,
572 })
573 }
574
575 #[cfg(test)]
576 #[inline]
577 pub fn unpack_serde(data: &[u8]) -> Result<Self, ()> {
578 match data.split_first() {
579 None => Err(()),
580 Some((&0u8, rest)) => bincode::deserialize(rest).map_err(|_| ()),
581 Some((_, _rest)) => Err(()),
582 }
583 }
584}
585
586pub fn initialize_market(
587 market: &Pubkey,
588 program_id: &Pubkey,
589 coin_mint_pk: &Pubkey,
590 pc_mint_pk: &Pubkey,
591 coin_vault_pk: &Pubkey,
592 pc_vault_pk: &Pubkey,
593 authority_pk: Option<&Pubkey>,
594 prune_authority_pk: Option<&Pubkey>,
595 consume_events_authority_pk: Option<&Pubkey>,
596 bids_pk: &Pubkey,
598 asks_pk: &Pubkey,
599 req_q_pk: &Pubkey,
600 event_q_pk: &Pubkey,
601 coin_lot_size: u64,
602 pc_lot_size: u64,
603 vault_signer_nonce: u64,
604 pc_dust_threshold: u64,
605) -> Result<solana_program::instruction::Instruction, DexError> {
606 let data = MarketInstruction::InitializeMarket(InitializeMarketInstruction {
607 coin_lot_size,
608 pc_lot_size,
609 fee_rate_bps: 0,
610 vault_signer_nonce,
611 pc_dust_threshold,
612 })
613 .pack();
614
615 let market_account = AccountMeta::new(*market, false);
616
617 let bids = AccountMeta::new(*bids_pk, false);
618 let asks = AccountMeta::new(*asks_pk, false);
619 let req_q = AccountMeta::new(*req_q_pk, false);
620 let event_q = AccountMeta::new(*event_q_pk, false);
621
622 let coin_vault = AccountMeta::new(*coin_vault_pk, false);
623 let pc_vault = AccountMeta::new(*pc_vault_pk, false);
624
625 let coin_mint = AccountMeta::new_readonly(*coin_mint_pk, false);
626 let pc_mint = AccountMeta::new_readonly(*pc_mint_pk, false);
627
628 let rent_sysvar = AccountMeta::new_readonly(solana_program::sysvar::rent::ID, false);
629
630 let mut accounts = vec![
631 market_account,
632 req_q,
633 event_q,
634 bids,
635 asks,
636 coin_vault,
637 pc_vault,
638 coin_mint,
640 pc_mint,
641 rent_sysvar,
643 ];
644 if let Some(auth) = authority_pk {
645 let authority = AccountMeta::new_readonly(*auth, false);
646 accounts.push(authority);
647 if let Some(prune_auth) = prune_authority_pk {
648 let authority = AccountMeta::new_readonly(*prune_auth, false);
649 accounts.push(authority);
650 if let Some(consume_events_auth) = consume_events_authority_pk {
651 let authority = AccountMeta::new_readonly(*consume_events_auth, false);
652 accounts.push(authority);
653 }
654 }
655 }
656
657 Ok(Instruction {
658 program_id: *program_id,
659 data,
660 accounts,
661 })
662}
663
664pub fn new_order(
665 market: &Pubkey,
666 open_orders_account: &Pubkey,
667 request_queue: &Pubkey,
668 event_queue: &Pubkey,
669 market_bids: &Pubkey,
670 market_asks: &Pubkey,
671 order_payer: &Pubkey,
672 open_orders_account_owner: &Pubkey,
673 coin_vault: &Pubkey,
674 pc_vault: &Pubkey,
675 spl_token_program_id: &Pubkey,
676 rent_sysvar_id: &Pubkey,
677 srm_account_referral: Option<&Pubkey>,
678 program_id: &Pubkey,
679 side: Side,
680 limit_price: NonZeroU64,
681 max_coin_qty: NonZeroU64,
682 order_type: OrderType,
683 client_order_id: u64,
684 self_trade_behavior: SelfTradeBehavior,
685 limit: u16,
686 max_native_pc_qty_including_fees: NonZeroU64,
687) -> Result<Instruction, DexError> {
688 let data = MarketInstruction::NewOrderV3(NewOrderInstructionV3 {
689 side,
690 limit_price,
691 max_coin_qty,
692 order_type,
693 client_order_id,
694 self_trade_behavior,
695 limit,
696 max_native_pc_qty_including_fees,
697 })
698 .pack();
699 let mut accounts = vec![
700 AccountMeta::new(*market, false),
701 AccountMeta::new(*open_orders_account, false),
702 AccountMeta::new(*request_queue, false),
703 AccountMeta::new(*event_queue, false),
704 AccountMeta::new(*market_bids, false),
705 AccountMeta::new(*market_asks, false),
706 AccountMeta::new(*order_payer, false),
707 AccountMeta::new_readonly(*open_orders_account_owner, true),
708 AccountMeta::new(*coin_vault, false),
709 AccountMeta::new(*pc_vault, false),
710 AccountMeta::new_readonly(*spl_token_program_id, false),
711 AccountMeta::new_readonly(*rent_sysvar_id, false),
712 ];
713 if let Some(key) = srm_account_referral {
714 accounts.push(AccountMeta::new_readonly(*key, false))
715 }
716 Ok(Instruction {
717 program_id: *program_id,
718 data,
719 accounts,
720 })
721}
722
723pub fn match_orders(
724 program_id: &Pubkey,
725 market: &Pubkey,
726 request_queue: &Pubkey,
727 bids: &Pubkey,
728 asks: &Pubkey,
729 event_queue: &Pubkey,
730 coin_fee_receivable_account: &Pubkey,
731 pc_fee_receivable_account: &Pubkey,
732 limit: u16,
733) -> Result<Instruction, DexError> {
734 let data = MarketInstruction::MatchOrders(limit).pack();
735 let accounts: Vec<AccountMeta> = vec![
736 AccountMeta::new(*market, false),
737 AccountMeta::new(*request_queue, false),
738 AccountMeta::new(*event_queue, false),
739 AccountMeta::new(*bids, false),
740 AccountMeta::new(*asks, false),
741 AccountMeta::new(*coin_fee_receivable_account, false),
742 AccountMeta::new(*pc_fee_receivable_account, false),
743 ];
744 Ok(Instruction {
745 program_id: *program_id,
746 data,
747 accounts,
748 })
749}
750
751pub fn consume_events(
752 program_id: &Pubkey,
753 open_orders_accounts: Vec<&Pubkey>,
754 market: &Pubkey,
755 event_queue: &Pubkey,
756 coin_fee_receivable_account: &Pubkey,
757 pc_fee_receivable_account: &Pubkey,
758 limit: u16,
759) -> Result<Instruction, DexError> {
760 let data = MarketInstruction::ConsumeEvents(limit).pack();
761 let mut accounts: Vec<AccountMeta> = open_orders_accounts
762 .iter()
763 .map(|key| AccountMeta::new(**key, false))
764 .collect();
765 accounts.append(&mut vec![
766 AccountMeta::new(*market, false),
767 AccountMeta::new(*event_queue, false),
768 AccountMeta::new(*coin_fee_receivable_account, false),
769 AccountMeta::new(*pc_fee_receivable_account, false),
770 ]);
771 Ok(Instruction {
772 program_id: *program_id,
773 data,
774 accounts,
775 })
776}
777
778pub fn consume_events_permissioned(
779 program_id: &Pubkey,
780 open_orders_accounts: Vec<&Pubkey>,
781 market: &Pubkey,
782 event_queue: &Pubkey,
783 consume_events_authority: &Pubkey,
784 limit: u16,
785) -> Result<Instruction, DexError> {
786 let data = MarketInstruction::ConsumeEventsPermissioned(limit).pack();
787 let mut accounts: Vec<AccountMeta> = open_orders_accounts
788 .iter()
789 .map(|key| AccountMeta::new(**key, false))
790 .collect();
791 accounts.append(&mut vec![
792 AccountMeta::new(*market, false),
793 AccountMeta::new(*event_queue, false),
794 AccountMeta::new_readonly(*consume_events_authority, true),
795 ]);
796 Ok(Instruction {
797 program_id: *program_id,
798 data,
799 accounts,
800 })
801}
802
803pub fn cancel_order(
804 program_id: &Pubkey,
805 market: &Pubkey,
806 market_bids: &Pubkey,
807 market_asks: &Pubkey,
808 open_orders_account: &Pubkey,
809 open_orders_account_owner: &Pubkey,
810 event_queue: &Pubkey,
811 side: Side,
812 order_id: u128,
813) -> Result<Instruction, DexError> {
814 let data = MarketInstruction::CancelOrderV2(CancelOrderInstructionV2 { side, order_id }).pack();
815 let accounts: Vec<AccountMeta> = vec![
816 AccountMeta::new(*market, false),
817 AccountMeta::new(*market_bids, false),
818 AccountMeta::new(*market_asks, false),
819 AccountMeta::new(*open_orders_account, false),
820 AccountMeta::new_readonly(*open_orders_account_owner, true),
821 AccountMeta::new(*event_queue, false),
822 ];
823 Ok(Instruction {
824 program_id: *program_id,
825 data,
826 accounts,
827 })
828}
829
830pub fn settle_funds(
831 program_id: &Pubkey,
832 market: &Pubkey,
833 spl_token_program_id: &Pubkey,
834 open_orders_account: &Pubkey,
835 open_orders_account_owner: &Pubkey,
836 coin_vault: &Pubkey,
837 coin_wallet: &Pubkey,
838 pc_vault: &Pubkey,
839 pc_wallet: &Pubkey,
840 referrer_pc_wallet: Option<&Pubkey>,
841 vault_signer: &Pubkey,
842) -> Result<Instruction, DexError> {
843 let data = MarketInstruction::SettleFunds.pack();
844 let mut accounts: Vec<AccountMeta> = vec![
845 AccountMeta::new(*market, false),
846 AccountMeta::new(*open_orders_account, false),
847 AccountMeta::new_readonly(*open_orders_account_owner, true),
848 AccountMeta::new(*coin_vault, false),
849 AccountMeta::new(*pc_vault, false),
850 AccountMeta::new(*coin_wallet, false),
851 AccountMeta::new(*pc_wallet, false),
852 AccountMeta::new_readonly(*vault_signer, false),
853 AccountMeta::new_readonly(*spl_token_program_id, false),
854 ];
855 if let Some(key) = referrer_pc_wallet {
856 accounts.push(AccountMeta::new(*key, false))
857 }
858 Ok(Instruction {
859 program_id: *program_id,
860 data,
861 accounts,
862 })
863}
864
865pub fn cancel_order_by_client_order_id(
866 program_id: &Pubkey,
867 market: &Pubkey,
868 market_bids: &Pubkey,
869 market_asks: &Pubkey,
870 open_orders_account: &Pubkey,
871 open_orders_account_owner: &Pubkey,
872 event_queue: &Pubkey,
873 client_order_id: u64,
874) -> Result<Instruction, DexError> {
875 let data = MarketInstruction::CancelOrderByClientIdV2(client_order_id).pack();
876 let accounts: Vec<AccountMeta> = vec![
877 AccountMeta::new(*market, false),
878 AccountMeta::new(*market_bids, false),
879 AccountMeta::new(*market_asks, false),
880 AccountMeta::new(*open_orders_account, false),
881 AccountMeta::new_readonly(*open_orders_account_owner, true),
882 AccountMeta::new(*event_queue, false),
883 ];
884 Ok(Instruction {
885 program_id: *program_id,
886 data,
887 accounts,
888 })
889}
890
891pub fn disable_market(
892 program_id: &Pubkey,
893 market: &Pubkey,
894 disable_authority_key: &Pubkey,
895) -> Result<Instruction, DexError> {
896 let data = MarketInstruction::DisableMarket.pack();
897 let accounts: Vec<AccountMeta> = vec![
898 AccountMeta::new(*market, false),
899 AccountMeta::new_readonly(*disable_authority_key, true),
900 ];
901 Ok(Instruction {
902 program_id: *program_id,
903 data,
904 accounts,
905 })
906}
907
908pub fn sweep_fees(
909 program_id: &Pubkey,
910 market: &Pubkey,
911 pc_vault: &Pubkey,
912 fee_sweeping_authority: &Pubkey,
913 fee_receivable_account: &Pubkey,
914 vault_signer: &Pubkey,
915 spl_token_program_id: &Pubkey,
916) -> Result<Instruction, DexError> {
917 let data = MarketInstruction::SweepFees.pack();
918 let accounts: Vec<AccountMeta> = vec![
919 AccountMeta::new(*market, false),
920 AccountMeta::new(*pc_vault, false),
921 AccountMeta::new_readonly(*fee_sweeping_authority, true),
922 AccountMeta::new(*fee_receivable_account, false),
923 AccountMeta::new_readonly(*vault_signer, false),
924 AccountMeta::new_readonly(*spl_token_program_id, false),
925 ];
926 Ok(Instruction {
927 program_id: *program_id,
928 data,
929 accounts,
930 })
931}
932
933pub fn close_open_orders(
934 program_id: &Pubkey,
935 open_orders: &Pubkey,
936 owner: &Pubkey,
937 destination: &Pubkey,
938 market: &Pubkey,
939) -> Result<Instruction, DexError> {
940 let data = MarketInstruction::CloseOpenOrders.pack();
941 let accounts: Vec<AccountMeta> = vec![
942 AccountMeta::new(*open_orders, false),
943 AccountMeta::new_readonly(*owner, true),
944 AccountMeta::new(*destination, false),
945 AccountMeta::new_readonly(*market, false),
946 ];
947 Ok(Instruction {
948 program_id: *program_id,
949 data,
950 accounts,
951 })
952}
953
954pub fn init_open_orders(
955 program_id: &Pubkey,
956 open_orders: &Pubkey,
957 owner: &Pubkey,
958 market: &Pubkey,
959 market_authority: Option<&Pubkey>,
960) -> Result<Instruction, DexError> {
961 let data = MarketInstruction::InitOpenOrders.pack();
962 let mut accounts: Vec<AccountMeta> = vec![
963 AccountMeta::new(*open_orders, false),
964 AccountMeta::new_readonly(*owner, true),
965 AccountMeta::new_readonly(*market, false),
966 AccountMeta::new_readonly(rent::ID, false),
967 ];
968 if let Some(market_authority) = market_authority {
969 accounts.push(AccountMeta::new_readonly(*market_authority, true));
970 }
971 Ok(Instruction {
972 program_id: *program_id,
973 data,
974 accounts,
975 })
976}
977
978pub fn prune(
979 program_id: &Pubkey,
980 market: &Pubkey,
981 bids: &Pubkey,
982 asks: &Pubkey,
983 prune_authority: &Pubkey,
984 open_orders: &Pubkey,
985 open_orders_owner: &Pubkey,
986 event_q: &Pubkey,
987 limit: u16,
988) -> Result<Instruction, DexError> {
989 let data = MarketInstruction::Prune(limit).pack();
990 let accounts: Vec<AccountMeta> = vec![
991 AccountMeta::new(*market, false),
992 AccountMeta::new(*bids, false),
993 AccountMeta::new(*asks, false),
994 AccountMeta::new_readonly(*prune_authority, true),
995 AccountMeta::new(*open_orders, false),
996 AccountMeta::new_readonly(*open_orders_owner, false),
997 AccountMeta::new(*event_q, false),
998 ];
999 Ok(Instruction {
1000 program_id: *program_id,
1001 data,
1002 accounts,
1003 })
1004}
1005
1006#[cfg(test)]
1007mod tests {
1008 use super::*;
1009
1010 proptest! {
1011 #[test]
1012 fn test_pack_unpack_roundtrip(inst: MarketInstruction) {
1013 let serialized = inst.pack();
1014 let unpack_serde_result = MarketInstruction::unpack_serde(&serialized).ok();
1015 let unpack_result = MarketInstruction::unpack(&serialized);
1016 assert_eq!(unpack_result, Some(inst));
1017 assert!(unpack_serde_result == unpack_result,
1018 "Serialized:\n{:?}\nLeft:\n{:#?}\nRight:\n{:#?}",
1019 serialized, unpack_serde_result, unpack_result
1020 );
1021 }
1022 }
1023}
1024
1025#[cfg(feature = "fuzz")]
1026mod fuzzing {
1027 use super::*;
1028 use crate::matching::{OrderType, Side};
1029 use arbitrary::Unstructured;
1030 use std::convert::{TryFrom, TryInto};
1031
1032 #[derive(arbitrary::Arbitrary)]
1033 struct NewOrderInstructionU64 {
1034 pub side: Side,
1035 pub limit_price: u64,
1036 pub max_qty: u64,
1037 pub order_type: OrderType,
1038 pub client_id: u64,
1039 pub self_trade_behavior: SelfTradeBehavior,
1040 }
1041
1042 #[derive(arbitrary::Arbitrary)]
1043 struct NewOrderInstructionV3U64 {
1044 pub side: Side,
1045
1046 pub limit_price: u64,
1047 pub max_coin_qty: u64,
1048 pub max_native_pc_qty_including_fees: u64,
1049 pub self_trade_behavior: SelfTradeBehavior,
1050 pub order_type: OrderType,
1051 pub client_order_id: u64,
1052 pub limit: u16,
1053 }
1054
1055 #[derive(arbitrary::Arbitrary)]
1056 struct SendTakeInstructionU64 {
1057 pub side: Side,
1058 pub limit_price: u64,
1059 pub max_coin_qty: u64,
1060 pub max_native_pc_qty_including_fees: u64,
1061 pub min_coin_qty: u64,
1062 pub min_native_pc_qty: u64,
1063 pub limit: u16,
1064 }
1065
1066 impl TryFrom<SendTakeInstructionU64> for SendTakeInstruction {
1067 type Error = std::num::TryFromIntError;
1068
1069 fn try_from(value: SendTakeInstructionU64) -> Result<Self, Self::Error> {
1070 Ok(Self {
1071 side: value.side,
1072 limit_price: value.limit_price.try_into()?,
1073 max_coin_qty: value.max_coin_qty.try_into()?,
1074 max_native_pc_qty_including_fees: value
1075 .max_native_pc_qty_including_fees
1076 .try_into()?,
1077 min_coin_qty: value.min_coin_qty,
1078 min_native_pc_qty: value.min_native_pc_qty,
1079 limit: value.limit,
1080 })
1081 }
1082 }
1083
1084 impl TryFrom<NewOrderInstructionV3U64> for NewOrderInstructionV3 {
1085 type Error = std::num::TryFromIntError;
1086
1087 fn try_from(value: NewOrderInstructionV3U64) -> Result<Self, Self::Error> {
1088 Ok(Self {
1089 side: value.side,
1090 limit_price: value.limit_price.try_into()?,
1091 max_coin_qty: value.max_coin_qty.try_into()?,
1092 max_native_pc_qty_including_fees: value
1093 .max_native_pc_qty_including_fees
1094 .try_into()?,
1095 order_type: value.order_type,
1096 client_order_id: value.client_order_id,
1097 self_trade_behavior: value.self_trade_behavior,
1098 limit: value.limit,
1099 })
1100 }
1101 }
1102
1103 impl TryFrom<NewOrderInstructionU64> for NewOrderInstructionV2 {
1104 type Error = std::num::TryFromIntError;
1105
1106 fn try_from(value: NewOrderInstructionU64) -> Result<Self, Self::Error> {
1107 Ok(Self {
1108 side: value.side,
1109 limit_price: value.limit_price.try_into()?,
1110 max_qty: value.max_qty.try_into()?,
1111 order_type: value.order_type,
1112 client_id: value.client_id,
1113 self_trade_behavior: value.self_trade_behavior,
1114 })
1115 }
1116 }
1117
1118 impl TryFrom<NewOrderInstructionU64> for NewOrderInstructionV1 {
1119 type Error = std::num::TryFromIntError;
1120
1121 fn try_from(value: NewOrderInstructionU64) -> Result<Self, Self::Error> {
1122 Ok(Self {
1123 side: value.side,
1124 limit_price: value.limit_price.try_into()?,
1125 max_qty: value.max_qty.try_into()?,
1126 order_type: value.order_type,
1127 client_id: value.client_id,
1128 })
1129 }
1130 }
1131
1132 impl From<&SendTakeInstruction> for SendTakeInstructionU64 {
1133 fn from(value: &SendTakeInstruction) -> Self {
1134 Self {
1135 side: value.side,
1136 limit_price: value.limit_price.into(),
1137 max_coin_qty: value.max_coin_qty.into(),
1138 max_native_pc_qty_including_fees: value.max_native_pc_qty_including_fees.into(),
1139 min_coin_qty: value.min_coin_qty,
1140 min_native_pc_qty: value.min_native_pc_qty,
1141 limit: value.limit,
1142 }
1143 }
1144 }
1145
1146 impl From<&NewOrderInstructionV3> for NewOrderInstructionV3U64 {
1147 fn from(value: &NewOrderInstructionV3) -> Self {
1148 Self {
1149 side: value.side,
1150 limit_price: value.limit_price.get(),
1151 max_coin_qty: value.max_coin_qty.get(),
1152 max_native_pc_qty_including_fees: value.max_native_pc_qty_including_fees.get(),
1153 self_trade_behavior: value.self_trade_behavior,
1154 order_type: value.order_type,
1155 client_order_id: value.client_order_id,
1156 limit: value.limit,
1157 }
1158 }
1159 }
1160
1161 impl From<&NewOrderInstructionV2> for NewOrderInstructionU64 {
1162 fn from(value: &NewOrderInstructionV2) -> Self {
1163 Self {
1164 side: value.side,
1165 limit_price: value.limit_price.get(),
1166 max_qty: value.max_qty.get(),
1167 order_type: value.order_type,
1168 client_id: value.client_id,
1169 self_trade_behavior: value.self_trade_behavior,
1170 }
1171 }
1172 }
1173
1174 impl From<&NewOrderInstructionV1> for NewOrderInstructionU64 {
1175 fn from(value: &NewOrderInstructionV1) -> Self {
1176 Self {
1177 side: value.side,
1178 limit_price: value.limit_price.get(),
1179 max_qty: value.max_qty.get(),
1180 order_type: value.order_type,
1181 client_id: value.client_id,
1182 self_trade_behavior: SelfTradeBehavior::DecrementTake,
1183 }
1184 }
1185 }
1186
1187 macro_rules! arbitrary_impl {
1188 ($T:ident, $TU64:ident) => {
1189 impl arbitrary::Arbitrary for $T {
1190 fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self, arbitrary::Error> {
1191 <$TU64 as arbitrary::Arbitrary>::arbitrary(u)?
1192 .try_into()
1193 .map_err(|_| arbitrary::Error::IncorrectFormat)
1194 }
1195
1196 fn size_hint(depth: usize) -> (usize, Option<usize>) {
1197 <$TU64 as arbitrary::Arbitrary>::size_hint(depth)
1198 }
1199
1200 fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
1201 let x: $TU64 = self.into();
1202 Box::new(x.shrink().map($TU64::try_into).filter_map(Result::ok))
1203 }
1204 }
1205 };
1206 }
1207
1208 arbitrary_impl!(SendTakeInstruction, SendTakeInstructionU64);
1209 arbitrary_impl!(NewOrderInstructionV3, NewOrderInstructionV3U64);
1210 arbitrary_impl!(NewOrderInstructionV2, NewOrderInstructionU64);
1211 arbitrary_impl!(NewOrderInstructionV1, NewOrderInstructionU64);
1212}