1use solana_sdk::{
7 instruction::{AccountMeta, Instruction},
8 pubkey::Pubkey,
9};
10
11fn system_program_id() -> Pubkey {
13 solana_system_interface::program::ID
14}
15
16use crate::program::constants::{
17 instruction, ASSOCIATED_TOKEN_PROGRAM_ID, INSTRUCTIONS_SYSVAR_ID, MAX_MAKERS,
18 TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID,
19};
20use crate::program::error::{SdkError, SdkResult};
21use crate::program::orders::FullOrder;
22use crate::program::pda::{
23 get_conditional_mint_pda, get_exchange_pda, get_market_pda, get_mint_authority_pda,
24 get_order_status_pda, get_position_pda, get_user_nonce_pda, get_vault_pda,
25};
26use crate::program::types::{
27 ActivateMarketParams, AddDepositMintParams, CreateMarketParams, MatchOrdersMultiParams,
28 MergeCompleteSetParams, MintCompleteSetParams, RedeemWinningsParams,
29 SettleMarketParams, WithdrawFromPositionParams,
30};
31use crate::program::utils::{
32 get_conditional_token_ata, get_deposit_token_ata, serialize_outcome_metadata,
33 validate_outcome_count, OutcomeMetadataInput,
34};
35
36fn signer_mut(pubkey: Pubkey) -> AccountMeta {
42 AccountMeta::new(pubkey, true)
43}
44
45fn writable(pubkey: Pubkey) -> AccountMeta {
47 AccountMeta::new(pubkey, false)
48}
49
50fn readonly(pubkey: Pubkey) -> AccountMeta {
52 AccountMeta::new_readonly(pubkey, false)
53}
54
55pub fn build_initialize_ix(authority: &Pubkey, program_id: &Pubkey) -> Instruction {
68 let (exchange, _) = get_exchange_pda(program_id);
69
70 let keys = vec![
71 signer_mut(*authority),
72 writable(exchange),
73 readonly(system_program_id()),
74 ];
75
76 let data = vec![instruction::INITIALIZE];
77
78 Instruction {
79 program_id: *program_id,
80 accounts: keys,
81 data,
82 }
83}
84
85pub fn build_create_market_ix(
95 params: &CreateMarketParams,
96 market_id: u64,
97 program_id: &Pubkey,
98) -> SdkResult<Instruction> {
99 validate_outcome_count(params.num_outcomes)?;
100
101 let (exchange, _) = get_exchange_pda(program_id);
102 let (market, _) = get_market_pda(market_id, program_id);
103
104 let keys = vec![
105 signer_mut(params.authority),
106 writable(exchange),
107 writable(market),
108 readonly(system_program_id()),
109 ];
110
111 let mut data = Vec::with_capacity(66);
113 data.push(instruction::CREATE_MARKET);
114 data.push(params.num_outcomes);
115 data.extend_from_slice(params.oracle.as_ref());
116 data.extend_from_slice(¶ms.question_id);
117
118 Ok(Instruction {
119 program_id: *program_id,
120 accounts: keys,
121 data,
122 })
123}
124
125pub fn build_add_deposit_mint_ix(
140 params: &AddDepositMintParams,
141 market: &Pubkey,
142 num_outcomes: u8,
143 program_id: &Pubkey,
144) -> SdkResult<Instruction> {
145 if params.outcome_metadata.len() != num_outcomes as usize {
146 return Err(SdkError::InvalidOutcomeCount { count: params.outcome_metadata.len() as u8 });
147 }
148
149 let (vault, _) = get_vault_pda(¶ms.deposit_mint, market, program_id);
150 let (mint_authority, _) = get_mint_authority_pda(market, program_id);
151
152 let mut keys = vec![
153 signer_mut(params.payer),
154 writable(*market),
155 readonly(params.deposit_mint),
156 writable(vault),
157 readonly(mint_authority),
158 readonly(TOKEN_PROGRAM_ID),
159 readonly(TOKEN_2022_PROGRAM_ID),
160 readonly(system_program_id()),
161 ];
162
163 for i in 0..num_outcomes {
165 let (mint, _) = get_conditional_mint_pda(market, ¶ms.deposit_mint, i, program_id);
166 keys.push(writable(mint));
167 }
168
169 let metadata_input: Vec<OutcomeMetadataInput> = params
171 .outcome_metadata
172 .iter()
173 .map(|m| OutcomeMetadataInput {
174 name: m.name.clone(),
175 symbol: m.symbol.clone(),
176 uri: m.uri.clone(),
177 })
178 .collect();
179
180 let mut data = vec![instruction::ADD_DEPOSIT_MINT];
181 data.extend(serialize_outcome_metadata(&metadata_input));
182
183 Ok(Instruction {
184 program_id: *program_id,
185 accounts: keys,
186 data,
187 })
188}
189
190pub fn build_mint_complete_set_ix(
210 params: &MintCompleteSetParams,
211 num_outcomes: u8,
212 program_id: &Pubkey,
213) -> Instruction {
214 let (exchange, _) = get_exchange_pda(program_id);
215 let (vault, _) = get_vault_pda(¶ms.deposit_mint, ¶ms.market, program_id);
216 let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
217 let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
218 let user_deposit_ata = get_deposit_token_ata(¶ms.user, ¶ms.deposit_mint);
219 let position_collateral_ata = get_deposit_token_ata(&position, ¶ms.deposit_mint);
220
221 let mut keys = vec![
222 signer_mut(params.user),
223 readonly(exchange),
224 readonly(params.market),
225 readonly(params.deposit_mint),
226 writable(vault),
227 writable(user_deposit_ata),
228 writable(position),
229 writable(position_collateral_ata),
230 readonly(mint_authority),
231 readonly(TOKEN_PROGRAM_ID),
232 readonly(TOKEN_2022_PROGRAM_ID),
233 readonly(ASSOCIATED_TOKEN_PROGRAM_ID),
234 readonly(system_program_id()),
235 ];
236
237 for i in 0..num_outcomes {
239 let (mint, _) =
240 get_conditional_mint_pda(¶ms.market, ¶ms.deposit_mint, i, program_id);
241 keys.push(writable(mint));
242 let position_ata = get_conditional_token_ata(&position, &mint);
243 keys.push(writable(position_ata));
244 }
245
246 let mut data = Vec::with_capacity(9);
248 data.push(instruction::MINT_COMPLETE_SET);
249 data.extend_from_slice(¶ms.amount.to_le_bytes());
250
251 Instruction {
252 program_id: *program_id,
253 accounts: keys,
254 data,
255 }
256}
257
258pub fn build_merge_complete_set_ix(
262 params: &MergeCompleteSetParams,
263 num_outcomes: u8,
264 program_id: &Pubkey,
265) -> Instruction {
266 let (exchange, _) = get_exchange_pda(program_id);
267 let (vault, _) = get_vault_pda(¶ms.deposit_mint, ¶ms.market, program_id);
268 let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
269 let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
270 let user_deposit_ata = get_deposit_token_ata(¶ms.user, ¶ms.deposit_mint);
271
272 let mut keys = vec![
273 signer_mut(params.user),
274 readonly(exchange),
275 readonly(params.market),
276 readonly(params.deposit_mint),
277 writable(vault),
278 writable(position),
279 writable(user_deposit_ata),
280 readonly(mint_authority),
281 readonly(TOKEN_PROGRAM_ID),
282 readonly(TOKEN_2022_PROGRAM_ID),
283 ];
284
285 for i in 0..num_outcomes {
287 let (mint, _) =
288 get_conditional_mint_pda(¶ms.market, ¶ms.deposit_mint, i, program_id);
289 keys.push(writable(mint));
290 let position_ata = get_conditional_token_ata(&position, &mint);
291 keys.push(writable(position_ata));
292 }
293
294 let mut data = Vec::with_capacity(9);
295 data.push(instruction::MERGE_COMPLETE_SET);
296 data.extend_from_slice(¶ms.amount.to_le_bytes());
297
298 Instruction {
299 program_id: *program_id,
300 accounts: keys,
301 data,
302 }
303}
304
305pub fn build_cancel_order_ix(
309 maker: &Pubkey,
310 order: &FullOrder,
311 program_id: &Pubkey,
312) -> Instruction {
313 let order_hash = order.hash();
314 let (order_status, _) = get_order_status_pda(&order_hash, program_id);
315
316 let keys = vec![
317 signer_mut(*maker),
318 writable(order_status),
319 readonly(system_program_id()),
320 ];
321
322 let mut data = Vec::with_capacity(258);
324 data.push(instruction::CANCEL_ORDER);
325 data.extend_from_slice(&order_hash);
326 data.extend_from_slice(&order.serialize());
327
328 Instruction {
329 program_id: *program_id,
330 accounts: keys,
331 data,
332 }
333}
334
335pub fn build_increment_nonce_ix(user: &Pubkey, program_id: &Pubkey) -> Instruction {
339 let (user_nonce, _) = get_user_nonce_pda(user, program_id);
340
341 let keys = vec![
342 signer_mut(*user),
343 writable(user_nonce),
344 readonly(system_program_id()),
345 ];
346
347 let data = vec![instruction::INCREMENT_NONCE];
348
349 Instruction {
350 program_id: *program_id,
351 accounts: keys,
352 data,
353 }
354}
355
356pub fn build_settle_market_ix(params: &SettleMarketParams, program_id: &Pubkey) -> Instruction {
360 let (exchange, _) = get_exchange_pda(program_id);
361 let (market, _) = get_market_pda(params.market_id, program_id);
362
363 let keys = vec![
364 signer_mut(params.oracle),
365 readonly(exchange),
366 writable(market),
367 ];
368
369 let data = vec![instruction::SETTLE_MARKET, params.winning_outcome];
370
371 Instruction {
372 program_id: *program_id,
373 accounts: keys,
374 data,
375 }
376}
377
378pub fn build_redeem_winnings_ix(
382 params: &RedeemWinningsParams,
383 winning_outcome: u8,
384 program_id: &Pubkey,
385) -> Instruction {
386 let (vault, _) = get_vault_pda(¶ms.deposit_mint, ¶ms.market, program_id);
387 let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
388 let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
389 let (winning_mint, _) = get_conditional_mint_pda(
390 ¶ms.market,
391 ¶ms.deposit_mint,
392 winning_outcome,
393 program_id,
394 );
395 let position_winning_ata = get_conditional_token_ata(&position, &winning_mint);
396 let user_deposit_ata = get_deposit_token_ata(¶ms.user, ¶ms.deposit_mint);
397
398 let keys = vec![
399 signer_mut(params.user),
400 readonly(params.market),
401 readonly(params.deposit_mint),
402 writable(vault),
403 writable(winning_mint),
404 writable(position),
405 writable(position_winning_ata),
406 writable(user_deposit_ata),
407 readonly(mint_authority),
408 readonly(TOKEN_PROGRAM_ID),
409 readonly(TOKEN_2022_PROGRAM_ID),
410 ];
411
412 let mut data = Vec::with_capacity(9);
413 data.push(instruction::REDEEM_WINNINGS);
414 data.extend_from_slice(¶ms.amount.to_le_bytes());
415
416 Instruction {
417 program_id: *program_id,
418 accounts: keys,
419 data,
420 }
421}
422
423pub fn build_set_paused_ix(
427 authority: &Pubkey,
428 paused: bool,
429 program_id: &Pubkey,
430) -> Instruction {
431 let (exchange, _) = get_exchange_pda(program_id);
432
433 let keys = vec![signer_mut(*authority), writable(exchange)];
434
435 let data = vec![instruction::SET_PAUSED, if paused { 1 } else { 0 }];
436
437 Instruction {
438 program_id: *program_id,
439 accounts: keys,
440 data,
441 }
442}
443
444pub fn build_set_operator_ix(
448 authority: &Pubkey,
449 new_operator: &Pubkey,
450 program_id: &Pubkey,
451) -> Instruction {
452 let (exchange, _) = get_exchange_pda(program_id);
453
454 let keys = vec![signer_mut(*authority), writable(exchange)];
455
456 let mut data = Vec::with_capacity(33);
457 data.push(instruction::SET_OPERATOR);
458 data.extend_from_slice(new_operator.as_ref());
459
460 Instruction {
461 program_id: *program_id,
462 accounts: keys,
463 data,
464 }
465}
466
467pub fn build_withdraw_from_position_ix(
471 params: &WithdrawFromPositionParams,
472 is_token_2022: bool,
473 program_id: &Pubkey,
474) -> Instruction {
475 let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
476 let position_ata = if is_token_2022 {
477 get_conditional_token_ata(&position, ¶ms.mint)
478 } else {
479 get_deposit_token_ata(&position, ¶ms.mint)
480 };
481 let user_ata = if is_token_2022 {
482 get_conditional_token_ata(¶ms.user, ¶ms.mint)
483 } else {
484 get_deposit_token_ata(¶ms.user, ¶ms.mint)
485 };
486 let token_program = if is_token_2022 {
487 TOKEN_2022_PROGRAM_ID
488 } else {
489 TOKEN_PROGRAM_ID
490 };
491
492 let keys = vec![
493 signer_mut(params.user),
494 writable(position),
495 readonly(params.mint),
496 writable(position_ata),
497 writable(user_ata),
498 readonly(token_program),
499 ];
500
501 let mut data = Vec::with_capacity(9);
502 data.push(instruction::WITHDRAW_FROM_POSITION);
503 data.extend_from_slice(¶ms.amount.to_le_bytes());
504
505 Instruction {
506 program_id: *program_id,
507 accounts: keys,
508 data,
509 }
510}
511
512pub fn build_activate_market_ix(
516 params: &ActivateMarketParams,
517 program_id: &Pubkey,
518) -> Instruction {
519 let (exchange, _) = get_exchange_pda(program_id);
520 let (market, _) = get_market_pda(params.market_id, program_id);
521
522 let keys = vec![
523 signer_mut(params.authority),
524 readonly(exchange),
525 writable(market),
526 ];
527
528 let data = vec![instruction::ACTIVATE_MARKET];
529
530 Instruction {
531 program_id: *program_id,
532 accounts: keys,
533 data,
534 }
535}
536
537pub fn build_match_orders_multi_ix(
558 params: &MatchOrdersMultiParams,
559 program_id: &Pubkey,
560) -> SdkResult<Instruction> {
561 if params.maker_orders.is_empty() {
562 return Err(SdkError::MissingField("maker_orders".to_string()));
563 }
564 if params.maker_orders.len() > MAX_MAKERS {
565 return Err(SdkError::TooManyMakers { count: params.maker_orders.len() });
566 }
567 if params.maker_orders.len() != params.fill_amounts.len() {
568 return Err(SdkError::MissingField("fill_amounts".to_string()));
569 }
570
571 let (exchange, _) = get_exchange_pda(program_id);
572 let taker_order_hash = params.taker_order.hash();
573 let (taker_order_status, _) = get_order_status_pda(&taker_order_hash, program_id);
574 let (taker_nonce, _) = get_user_nonce_pda(¶ms.taker_order.maker, program_id);
575 let (taker_position, _) =
576 get_position_pda(¶ms.taker_order.maker, ¶ms.market, program_id);
577 let taker_base_ata = get_conditional_token_ata(&taker_position, ¶ms.base_mint);
578 let taker_quote_ata = get_conditional_token_ata(&taker_position, ¶ms.quote_mint);
579
580 let mut keys = vec![
581 signer_mut(params.operator),
582 readonly(exchange),
583 readonly(params.market),
584 writable(taker_order_status),
585 readonly(taker_nonce),
586 writable(taker_position),
587 readonly(params.base_mint),
588 readonly(params.quote_mint),
589 writable(taker_base_ata),
590 writable(taker_quote_ata),
591 readonly(TOKEN_2022_PROGRAM_ID),
592 readonly(system_program_id()),
593 readonly(INSTRUCTIONS_SYSVAR_ID),
594 ];
595
596 for maker_order in ¶ms.maker_orders {
598 let maker_order_hash = maker_order.hash();
599 let (maker_order_status, _) = get_order_status_pda(&maker_order_hash, program_id);
600 let (maker_nonce, _) = get_user_nonce_pda(&maker_order.maker, program_id);
601 let (maker_position, _) = get_position_pda(&maker_order.maker, ¶ms.market, program_id);
602 let maker_base_ata = get_conditional_token_ata(&maker_position, ¶ms.base_mint);
603 let maker_quote_ata = get_conditional_token_ata(&maker_position, ¶ms.quote_mint);
604
605 keys.push(writable(maker_order_status));
606 keys.push(readonly(maker_nonce));
607 keys.push(writable(maker_position));
608 keys.push(writable(maker_base_ata));
609 keys.push(writable(maker_quote_ata));
610 }
611
612 let taker_compact = params.taker_order.to_compact();
616 let num_makers = params.maker_orders.len() as u8;
617
618 let data_size = 1 + 32 + 65 + 64 + 1 + (params.maker_orders.len() * (32 + 65 + 64 + 8));
619 let mut data = Vec::with_capacity(data_size);
620
621 data.push(instruction::MATCH_ORDERS_MULTI);
622 data.extend_from_slice(&taker_order_hash);
623 data.extend_from_slice(&taker_compact.serialize());
624 data.extend_from_slice(¶ms.taker_order.signature);
625 data.push(num_makers);
626
627 for (i, maker_order) in params.maker_orders.iter().enumerate() {
628 let maker_hash = maker_order.hash();
629 let maker_compact = maker_order.to_compact();
630
631 data.extend_from_slice(&maker_hash);
632 data.extend_from_slice(&maker_compact.serialize());
633 data.extend_from_slice(&maker_order.signature);
634 data.extend_from_slice(¶ms.fill_amounts[i].to_le_bytes());
635 }
636
637 Ok(Instruction {
638 program_id: *program_id,
639 accounts: keys,
640 data,
641 })
642}
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647 use crate::program::constants::PROGRAM_ID;
648
649 fn test_program_id() -> Pubkey {
650 *PROGRAM_ID
651 }
652
653 #[test]
654 fn test_build_initialize_ix() {
655 let authority = Pubkey::new_unique();
656 let program_id = test_program_id();
657
658 let ix = build_initialize_ix(&authority, &program_id);
659
660 assert_eq!(ix.program_id, program_id);
661 assert_eq!(ix.accounts.len(), 3);
662 assert_eq!(ix.data, vec![instruction::INITIALIZE]);
663 }
664
665 #[test]
666 fn test_build_increment_nonce_ix() {
667 let user = Pubkey::new_unique();
668 let program_id = test_program_id();
669
670 let ix = build_increment_nonce_ix(&user, &program_id);
671
672 assert_eq!(ix.program_id, program_id);
673 assert_eq!(ix.accounts.len(), 3);
674 assert_eq!(ix.data, vec![instruction::INCREMENT_NONCE]);
675 }
676
677 #[test]
678 fn test_build_set_paused_ix() {
679 let authority = Pubkey::new_unique();
680 let program_id = test_program_id();
681
682 let ix_pause = build_set_paused_ix(&authority, true, &program_id);
683 assert_eq!(ix_pause.data, vec![instruction::SET_PAUSED, 1]);
684
685 let ix_unpause = build_set_paused_ix(&authority, false, &program_id);
686 assert_eq!(ix_unpause.data, vec![instruction::SET_PAUSED, 0]);
687 }
688
689 #[test]
690 fn test_build_set_operator_ix() {
691 let authority = Pubkey::new_unique();
692 let new_operator = Pubkey::new_unique();
693 let program_id = test_program_id();
694
695 let ix = build_set_operator_ix(&authority, &new_operator, &program_id);
696
697 assert_eq!(ix.data.len(), 33);
698 assert_eq!(ix.data[0], instruction::SET_OPERATOR);
699 assert_eq!(&ix.data[1..33], new_operator.as_ref());
700 }
701
702 #[test]
703 fn test_build_create_market_ix() {
704 let params = CreateMarketParams {
705 authority: Pubkey::new_unique(),
706 num_outcomes: 3,
707 oracle: Pubkey::new_unique(),
708 question_id: [42u8; 32],
709 };
710 let program_id = test_program_id();
711
712 let ix = build_create_market_ix(¶ms, 0, &program_id).unwrap();
713
714 assert_eq!(ix.accounts.len(), 4);
715 assert_eq!(ix.data.len(), 66); assert_eq!(ix.data[0], instruction::CREATE_MARKET);
717 assert_eq!(ix.data[1], 3);
718 }
719
720 #[test]
721 fn test_build_create_market_invalid_outcomes() {
722 let params = CreateMarketParams {
723 authority: Pubkey::new_unique(),
724 num_outcomes: 7, oracle: Pubkey::new_unique(),
726 question_id: [0u8; 32],
727 };
728 let program_id = test_program_id();
729
730 let result = build_create_market_ix(¶ms, 0, &program_id);
731 assert!(result.is_err());
732 }
733
734 #[test]
735 fn test_build_activate_market_ix() {
736 let params = ActivateMarketParams {
737 authority: Pubkey::new_unique(),
738 market_id: 5,
739 };
740 let program_id = test_program_id();
741
742 let ix = build_activate_market_ix(¶ms, &program_id);
743
744 assert_eq!(ix.accounts.len(), 3);
745 assert_eq!(ix.data, vec![instruction::ACTIVATE_MARKET]);
746 }
747
748 #[test]
749 fn test_build_settle_market_ix() {
750 let params = SettleMarketParams {
751 oracle: Pubkey::new_unique(),
752 market_id: 1,
753 winning_outcome: 2,
754 };
755 let program_id = test_program_id();
756
757 let ix = build_settle_market_ix(¶ms, &program_id);
758
759 assert_eq!(ix.accounts.len(), 3);
760 assert_eq!(ix.data.len(), 2);
761 assert_eq!(ix.data[0], instruction::SETTLE_MARKET);
762 assert_eq!(ix.data[1], 2);
763 }
764}