1use serde::{Deserialize, Serialize};
2
3use crate::common::LiquidityUnavailableReason;
4use crate::endorsement::Endorsement;
5use crate::payment_in_lieu::PaymentInLieuToken;
6
7#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
8pub enum FeePayer {
9 #[serde(rename = "retailTrader")]
10 RetailTrader,
11 #[serde(rename = "marketMaker")]
12 MarketMaker,
13}
14
15#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
16#[serde(rename_all = "camelCase")]
17pub struct IndicativeQuoteRequestParams {
18 pub send_mint: String,
20 pub receive_mint: String,
22 pub send_qty: String,
24 pub order_flow_source: String,
26 pub endorsement: Endorsement,
28 #[serde(skip_serializing_if = "Option::is_none")]
32 pub fee_payer: Option<FeePayer>,
33}
34
35#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
36#[serde(tag = "type", content = "data")]
37pub enum IndicativeQuoteResponse {
38 Ok(IndicativeQuoteOkResponse),
39 Unavailable(IndicativeQuoteUnavailableResponse),
40}
41
42#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
43#[serde(rename_all = "camelCase")]
44pub struct IndicativeQuoteOkResponse {
45 pub fill_price: String,
47 pub effective_platform_fee_bps: u16,
48 pub auction_id: u64,
49 pub auction_epoch: u64,
50}
51
52#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
53#[serde(rename_all = "camelCase")]
54pub struct IndicativeQuoteUnavailableResponse {
55 pub reason: LiquidityUnavailableReason,
56}
57
58#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct FirmQuoteRequestParams {
61 pub send_mint: String,
63 pub receive_mint: String,
65 pub send_qty: String,
67 #[serde(rename = "useNativeSOL", skip_serializing_if = "Option::is_none")]
69 pub use_native_sol: Option<bool>,
70 pub order_flow_source: String,
72 pub endorsement: Endorsement,
74 #[serde(skip_serializing_if = "Option::is_none")]
78 pub fee_payer: Option<FeePayer>,
79}
80
81#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
82#[serde(tag = "type", content = "data")]
83pub enum FirmQuoteResponse {
84 Ok(Box<FirmQuoteOkResponse>),
85 PaymentInLieu(Box<FirmQuotePaymentInLieuResponse>),
86 Unavailable(Box<FirmQuoteUnavailableResponse>),
87}
88
89#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
90#[serde(rename_all = "camelCase")]
91pub struct FirmQuoteOkResponse {
92 pub tx: String,
95 pub tx_required_lamports: u64,
98 pub solana_transaction_fee: u64,
100 pub token_account_rent_deposit: u64,
104 pub last_valid_block_height: u64,
106 pub last_allowed_block_height: u64,
108 pub block_height_query_commitment: String,
112 pub send_qty: String,
114 pub receive_qty: String,
116 pub min_fill_qty: String,
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub platform_fee: Option<PlatformFee>,
121 pub dbbo_time: u64,
123 pub dbbo_data: serde_json::Value,
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub send_notional: Option<f64>,
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub receive_notional: Option<f64>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub fill_notional: Option<f64>,
135 pub request_id: SignatoryRequestId,
137}
138
139#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
140#[serde(rename_all = "camelCase")]
141pub struct SignatoryRequestId {
142 pub signature: String,
146 pub id: String,
148 pub retail_trader: String,
149 pub notional: u64,
150 pub auction_id: u64,
151 pub auction_epoch: u64,
152 #[serde(rename = "marketMakerURL")]
153 pub market_maker_url: String,
154 pub quote_id: String,
155 pub endorsement: Endorsement,
156 pub last_valid_block_height: u64,
157 pub last_allowed_block_height: u64,
158 pub tx: String,
159}
160
161#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
162#[serde(rename_all = "camelCase")]
163pub struct PlatformFee {
164 pub receiver: String,
165 pub qty: String,
168 pub bps: u16,
169 pub mint: String,
171}
172
173#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
174#[serde(rename_all = "camelCase")]
175pub struct FirmQuotePaymentInLieuResponse {
176 pub reason: LiquidityUnavailableReason,
177 pub payment_in_lieu_token: PaymentInLieuToken,
178}
179
180#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
181#[serde(rename_all = "camelCase")]
182pub struct FirmQuoteUnavailableResponse {
183 pub reason: LiquidityUnavailableReason,
184}
185
186#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
187#[serde(rename_all = "camelCase")]
188pub struct SendTransactionRequestParams {
189 pub tx: String,
191 pub request_id: SignatoryRequestId,
193}
194
195#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
196#[serde(tag = "type", content = "data")]
197pub enum SendTransactionResponse {
198 Sent(SendTransactionSentResponse),
199 NotSent,
200}
201
202#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
203#[serde(rename_all = "camelCase")]
204pub struct SendTransactionSentResponse {
205 pub signature: String,
206}
207
208#[cfg(test)]
209mod tests {
210 use assert_json_diff::assert_json_eq;
211 use serde_json::{json, Map, Value};
212
213 use super::*;
214
215 #[test]
216 fn test_serde_indicative_quote_ok_response() {
217 let obj = IndicativeQuoteResponse::Ok(IndicativeQuoteOkResponse {
218 fill_price: "100000".into(),
219 effective_platform_fee_bps: 50,
220 auction_id: 1,
221 auction_epoch: 2,
222 });
223 let expected = json!({
224 "type": "Ok",
225 "data": {
226 "fillPrice": "100000",
227 "effectivePlatformFeeBps": 50,
228 "auctionId": 1,
229 "auctionEpoch": 2,
230 },
231 });
232 let serialized = serde_json::to_value(&obj).unwrap();
233 assert_json_eq!(serialized, expected);
234
235 let deserialized: IndicativeQuoteResponse = serde_json::from_value(expected).unwrap();
236 assert_eq!(deserialized, obj);
237 }
238
239 #[test]
240 fn test_serde_indicative_quote_unavailable_response() {
241 let obj = IndicativeQuoteResponse::Unavailable(IndicativeQuoteUnavailableResponse {
242 reason: LiquidityUnavailableReason {
243 code: 1,
244 msg: "msg".into(),
245 },
246 });
247 let expected = json!({
248 "type": "Unavailable",
249 "data": {
250 "reason": {
251 "code": 1,
252 "msg": "msg",
253 },
254 },
255 });
256 let serialized = serde_json::to_value(&obj).unwrap();
257 assert_json_eq!(serialized, expected);
258
259 let deserialized: IndicativeQuoteResponse = serde_json::from_value(expected).unwrap();
260 assert_eq!(deserialized, obj);
261 }
262
263 #[test]
264 fn test_serde_firm_quote_ok_response() {
265 let obj = FirmQuoteResponse::Ok(Box::new(FirmQuoteOkResponse {
266 tx: "tx".into(),
267 tx_required_lamports: 1,
268 solana_transaction_fee: 1,
269 token_account_rent_deposit: 1,
270 last_valid_block_height: 1,
271 last_allowed_block_height: 1,
272 block_height_query_commitment: "block_height_query_commitment".into(),
273 send_qty: "5000000".into(),
274 receive_qty: "2000000".into(),
275 min_fill_qty: "2000000".into(),
276 platform_fee: Some(PlatformFee {
277 receiver: "receiver".into(),
278 qty: "17000".into(),
279 bps: 85,
280 mint: "mint".into(),
281 }),
282 dbbo_time: 1,
283 dbbo_data: json!({}),
284 send_notional: Some(0.52),
285 receive_notional: Some(0.52),
286 fill_notional: Some(0.52),
287 request_id: SignatoryRequestId {
288 signature: "signature".into(),
289 id: "id".into(),
290 retail_trader: "retail_trader".into(),
291 notional: 1,
292 auction_id: 1,
293 auction_epoch: 1,
294 market_maker_url: "market_maker_url".into(),
295 quote_id: "quote_id".into(),
296 endorsement: Endorsement {
297 endorser: "endorser".into(),
298 signature: "signature".into(),
299 id: "id".into(),
300 expiration_time_utc: 1,
301 data: "data".into(),
302 },
303 last_valid_block_height: 1,
304 last_allowed_block_height: 1,
305 tx: "tx".into(),
306 },
307 }));
308 let expected = json!({
309 "type": "Ok",
310 "data": {
311 "tx": "tx",
312 "txRequiredLamports": 1,
313 "solanaTransactionFee": 1,
314 "tokenAccountRentDeposit": 1,
315 "lastValidBlockHeight": 1,
316 "lastAllowedBlockHeight": 1,
317 "blockHeightQueryCommitment": "block_height_query_commitment",
318 "sendQty": "5000000",
319 "receiveQty": "2000000",
320 "minFillQty": "2000000",
321 "platformFee": {
322 "receiver": "receiver",
323 "qty": "17000",
324 "bps": 85,
325 "mint": "mint",
326 },
327 "dbboTime": 1,
328 "dbboData": Value::Object(Map::new()),
329 "sendNotional": 0.52,
330 "receiveNotional": 0.52,
331 "fillNotional": 0.52,
332 "requestId": {
333 "signature": "signature",
334 "id": "id",
335 "retailTrader": "retail_trader",
336 "notional": 1,
337 "auctionId": 1,
338 "auctionEpoch": 1,
339 "marketMakerURL": "market_maker_url",
340 "quoteId": "quote_id",
341 "endorsement": {
342 "endorser": "endorser",
343 "signature": "signature",
344 "id": "id",
345 "expirationTimeUTC": 1,
346 "data": "data",
347 },
348 "lastValidBlockHeight": 1,
349 "lastAllowedBlockHeight": 1,
350 "tx": "tx",
351 },
352 },
353 });
354 let serialized = serde_json::to_value(&obj).unwrap();
355 assert_json_eq!(serialized, expected);
356 let deserialized: FirmQuoteResponse = serde_json::from_value(expected).unwrap();
357 assert_eq!(deserialized, obj);
358 }
359
360 #[test]
361 fn test_serde_firm_quote_ok_response_omit_none() {
362 let obj = FirmQuoteResponse::Ok(Box::new(FirmQuoteOkResponse {
363 tx: "tx".into(),
364 tx_required_lamports: 1,
365 solana_transaction_fee: 1,
366 token_account_rent_deposit: 1,
367 last_valid_block_height: 1,
368 last_allowed_block_height: 1,
369 block_height_query_commitment: "block_height_query_commitment".into(),
370 send_qty: "5000000".into(),
371 receive_qty: "2000000".into(),
372 min_fill_qty: "2000000".into(),
373 platform_fee: None,
374 dbbo_time: 1,
375 dbbo_data: json!({}),
376 send_notional: None,
377 receive_notional: None,
378 fill_notional: None,
379 request_id: SignatoryRequestId {
380 signature: "signature".into(),
381 id: "id".into(),
382 retail_trader: "retail_trader".into(),
383 notional: 1,
384 auction_id: 1,
385 auction_epoch: 1,
386 market_maker_url: "market_maker_url".into(),
387 quote_id: "quote_id".into(),
388 endorsement: Endorsement {
389 endorser: "endorser".into(),
390 signature: "signature".into(),
391 id: "id".into(),
392 expiration_time_utc: 1,
393 data: "data".into(),
394 },
395 last_valid_block_height: 1,
396 last_allowed_block_height: 1,
397 tx: "tx".into(),
398 },
399 }));
400 let expected = json!({
401 "type": "Ok",
402 "data": {
403 "tx": "tx",
404 "txRequiredLamports": 1,
405 "solanaTransactionFee": 1,
406 "tokenAccountRentDeposit": 1,
407 "lastValidBlockHeight": 1,
408 "lastAllowedBlockHeight": 1,
409 "blockHeightQueryCommitment": "block_height_query_commitment",
410 "sendQty": "5000000",
411 "receiveQty": "2000000",
412 "minFillQty": "2000000",
413 "dbboTime": 1,
414 "dbboData": Value::Object(Map::new()),
415 "requestId": {
416 "signature": "signature",
417 "id": "id",
418 "retailTrader": "retail_trader",
419 "notional": 1,
420 "auctionId": 1,
421 "auctionEpoch": 1,
422 "marketMakerURL": "market_maker_url",
423 "quoteId": "quote_id",
424 "endorsement": {
425 "endorser": "endorser",
426 "signature": "signature",
427 "id": "id",
428 "expirationTimeUTC": 1,
429 "data": "data",
430 },
431 "lastValidBlockHeight": 1,
432 "lastAllowedBlockHeight": 1,
433 "tx": "tx",
434 },
435 },
436 });
437 let serialized = serde_json::to_value(&obj).unwrap();
438 assert_json_eq!(serialized, expected);
439 let deserialized: FirmQuoteResponse = serde_json::from_value(expected).unwrap();
440 assert_eq!(deserialized, obj);
441 }
442
443 #[test]
444 fn test_serde_firm_quote_payment_in_lieu_response() {
445 let obj = FirmQuoteResponse::PaymentInLieu(Box::new(FirmQuotePaymentInLieuResponse {
446 reason: LiquidityUnavailableReason {
447 code: 1,
448 msg: "msg".into(),
449 },
450 payment_in_lieu_token: PaymentInLieuToken {
451 issuer: "issuer".into(),
452 signature: "signature".into(),
453 id: "id".into(),
454 notional: 1,
455 auction_id: 2,
456 auction_epoch: 3,
457 market_maker: "market_maker".into(),
458 endorsement: Endorsement {
459 endorser: "endorser".into(),
460 signature: "signature".into(),
461 id: "id".into(),
462 expiration_time_utc: 1,
463 data: "data".into(),
464 },
465 },
466 }));
467 let expected = json!({
468 "type": "PaymentInLieu",
469 "data": {
470 "reason": {
471 "code": 1,
472 "msg": "msg",
473 },
474 "paymentInLieuToken": {
475 "issuer": "issuer",
476 "signature": "signature",
477 "id": "id",
478 "notional": 1,
479 "auctionId": 2,
480 "auctionEpoch": 3,
481 "marketMaker": "market_maker",
482 "endorsement": {
483 "endorser": "endorser",
484 "signature": "signature",
485 "id": "id",
486 "expirationTimeUTC": 1,
487 "data": "data",
488 },
489 },
490 },
491 });
492 let serialized = serde_json::to_value(&obj).unwrap();
493 assert_json_eq!(serialized, expected);
494 let deserialized: FirmQuoteResponse = serde_json::from_value(expected).unwrap();
495 assert_eq!(deserialized, obj);
496 }
497
498 #[test]
499 fn test_serde_firm_quote_unavailable_response() {
500 let obj = FirmQuoteResponse::Unavailable(Box::new(FirmQuoteUnavailableResponse {
501 reason: LiquidityUnavailableReason {
502 code: 1,
503 msg: "msg".into(),
504 },
505 }));
506 let expected = json!({
507 "type": "Unavailable",
508 "data": {
509 "reason": {
510 "code": 1,
511 "msg": "msg",
512 },
513 },
514 });
515 let serialized = serde_json::to_value(&obj).unwrap();
516 assert_json_eq!(serialized, expected);
517 let deserialized: FirmQuoteResponse = serde_json::from_value(expected).unwrap();
518 assert_eq!(deserialized, obj);
519 }
520
521 #[test]
522 fn test_serde_send_transaction_sent_response() {
523 let obj = SendTransactionResponse::Sent(SendTransactionSentResponse {
524 signature: "signature".into(),
525 });
526 let expected = json!({
527 "type": "Sent",
528 "data": {
529 "signature": "signature",
530 },
531 });
532 let serialized = serde_json::to_value(&obj).unwrap();
533 assert_json_eq!(serialized, expected);
534 let deserialized: SendTransactionResponse = serde_json::from_value(expected).unwrap();
535 assert_eq!(deserialized, obj);
536 }
537
538 #[test]
539 fn test_serde_send_transaction_not_sent_response() {
540 let obj = SendTransactionResponse::NotSent;
541 let expected = json!({
542 "type": "NotSent",
543 });
544 let serialized = serde_json::to_value(&obj).unwrap();
545 assert_json_eq!(serialized, expected);
546 let deserialized: SendTransactionResponse = serde_json::from_value(expected).unwrap();
547 assert_eq!(deserialized, obj);
548 }
549
550 #[test]
551 fn test_serde_indicative_quote_request() {
552 let obj = IndicativeQuoteRequestParams {
553 send_mint: "send_mint".into(),
554 receive_mint: "receive_mint".into(),
555 send_qty: "5000000".into(),
556 order_flow_source: "order_flow_source".into(),
557 endorsement: Endorsement {
558 endorser: "endorser".into(),
559 signature: "signature".into(),
560 id: "id".into(),
561 expiration_time_utc: 1,
562 data: "data".into(),
563 },
564 fee_payer: Some(FeePayer::RetailTrader),
565 };
566 let expected = json!({
567 "sendMint": "send_mint",
568 "receiveMint": "receive_mint",
569 "sendQty": "5000000",
570 "orderFlowSource": "order_flow_source",
571 "endorsement": {
572 "endorser": "endorser",
573 "signature": "signature",
574 "id": "id",
575 "expirationTimeUTC": 1,
576 "data": "data",
577 },
578 "feePayer": "retailTrader",
579 });
580 let serialized = serde_json::to_value(&obj).unwrap();
581 assert_json_eq!(serialized, expected);
582 let deserialized: IndicativeQuoteRequestParams = serde_json::from_value(expected).unwrap();
583 assert_eq!(deserialized, obj);
584
585 let obj = IndicativeQuoteRequestParams {
586 fee_payer: Some(FeePayer::MarketMaker),
587 ..obj
588 };
589 let expected = json!({
590 "sendMint": "send_mint",
591 "receiveMint": "receive_mint",
592 "sendQty": "5000000",
593 "orderFlowSource": "order_flow_source",
594 "endorsement": {
595 "endorser": "endorser",
596 "signature": "signature",
597 "id": "id",
598 "expirationTimeUTC": 1,
599 "data": "data",
600 },
601 "feePayer": "marketMaker",
602 });
603 let serialized = serde_json::to_value(&obj).unwrap();
604 assert_json_eq!(serialized, expected);
605 let deserialized: IndicativeQuoteRequestParams = serde_json::from_value(expected).unwrap();
606 assert_eq!(deserialized, obj);
607 }
608
609 #[test]
610 fn test_serde_indicative_quote_request_omit_none() {
611 let obj = IndicativeQuoteRequestParams {
612 send_mint: "send_mint".into(),
613 receive_mint: "receive_mint".into(),
614 send_qty: "5000000".into(),
615 order_flow_source: "order_flow_source".into(),
616 endorsement: Endorsement {
617 endorser: "endorser".into(),
618 signature: "signature".into(),
619 id: "id".into(),
620 expiration_time_utc: 1,
621 data: "data".into(),
622 },
623 fee_payer: None,
624 };
625 let expected = json!({
626 "sendMint": "send_mint",
627 "receiveMint": "receive_mint",
628 "sendQty": "5000000",
629 "orderFlowSource": "order_flow_source",
630 "endorsement": {
631 "endorser": "endorser",
632 "signature": "signature",
633 "id": "id",
634 "expirationTimeUTC": 1,
635 "data": "data",
636 },
637 });
638 let serialized = serde_json::to_value(&obj).unwrap();
639 assert_json_eq!(serialized, expected);
640 let deserialized: IndicativeQuoteRequestParams = serde_json::from_value(expected).unwrap();
641 assert_eq!(deserialized, obj);
642 }
643
644 #[test]
645 fn test_serde_firm_quote_request() {
646 let obj = FirmQuoteRequestParams {
647 send_mint: "send_mint".into(),
648 receive_mint: "receive_mint".into(),
649 send_qty: "5000000".into(),
650 use_native_sol: Some(false),
651 order_flow_source: "order_flow_source".into(),
652 endorsement: Endorsement {
653 endorser: "endorser".into(),
654 signature: "signature".into(),
655 id: "id".into(),
656 expiration_time_utc: 1,
657 data: "data".into(),
658 },
659 fee_payer: Some(FeePayer::RetailTrader),
660 };
661 let expected = json!({
662 "sendMint": "send_mint",
663 "receiveMint": "receive_mint",
664 "sendQty": "5000000",
665 "useNativeSOL": false,
666 "orderFlowSource": "order_flow_source",
667 "endorsement": {
668 "endorser": "endorser",
669 "signature": "signature",
670 "id": "id",
671 "expirationTimeUTC": 1,
672 "data": "data",
673 },
674 "feePayer": "retailTrader",
675 });
676 let serialized = serde_json::to_value(&obj).unwrap();
677 assert_json_eq!(serialized, expected);
678 let deserialized: FirmQuoteRequestParams = serde_json::from_value(expected).unwrap();
679 assert_eq!(deserialized, obj);
680
681 let obj = FirmQuoteRequestParams {
682 fee_payer: Some(FeePayer::MarketMaker),
683 ..obj
684 };
685 let expected = json!({
686 "sendMint": "send_mint",
687 "receiveMint": "receive_mint",
688 "sendQty": "5000000",
689 "useNativeSOL": false,
690 "orderFlowSource": "order_flow_source",
691 "endorsement": {
692 "endorser": "endorser",
693 "signature": "signature",
694 "id": "id",
695 "expirationTimeUTC": 1,
696 "data": "data",
697 },
698 "feePayer": "marketMaker",
699 });
700 let serialized = serde_json::to_value(&obj).unwrap();
701 assert_json_eq!(serialized, expected);
702 let deserialized: FirmQuoteRequestParams = serde_json::from_value(expected).unwrap();
703 assert_eq!(deserialized, obj);
704 }
705
706 #[test]
707 fn test_serde_firm_quote_request_omit_none() {
708 let obj = FirmQuoteRequestParams {
709 send_mint: "send_mint".into(),
710 receive_mint: "receive_mint".into(),
711 send_qty: "5000000".into(),
712 use_native_sol: None,
713 order_flow_source: "order_flow_source".into(),
714 endorsement: Endorsement {
715 endorser: "endorser".into(),
716 signature: "signature".into(),
717 id: "id".into(),
718 expiration_time_utc: 1,
719 data: "data".into(),
720 },
721 fee_payer: None,
722 };
723 let expected = json!({
724 "sendMint": "send_mint",
725 "receiveMint": "receive_mint",
726 "sendQty": "5000000",
727 "orderFlowSource": "order_flow_source",
728 "endorsement": {
729 "endorser": "endorser",
730 "signature": "signature",
731 "id": "id",
732 "expirationTimeUTC": 1,
733 "data": "data",
734 },
735 });
736 let serialized = serde_json::to_value(&obj).unwrap();
737 assert_json_eq!(serialized, expected);
738 let deserialized: FirmQuoteRequestParams = serde_json::from_value(expected).unwrap();
739 assert_eq!(deserialized, obj);
740 }
741}