renegade_sdk/external_match_client/
mod.rs

1//! A client for requesting external matches from the relayer
2//!
3//! An external match is one between an internal party -- one with state
4//! committed into the Renegade darkpool, and an external party -- one with no
5//! state committed into the Renegade darkpool.
6
7pub mod api_types;
8
9mod client;
10mod options;
11mod v1_client;
12mod v1_conversions;
13use api_types::{Amount, ExternalOrderV2, OrderSide, v1_types};
14pub use client::ExternalMatchClient;
15#[allow(deprecated)]
16pub use options::{
17    AssembleQuoteOptions, AssembleQuoteOptionsV2, ExternalMatchOptions, RequestQuoteOptions,
18};
19
20mod error;
21pub use error::ExternalMatchClientError;
22
23/// The auth server query param for requesting gas sponsorship
24pub const GAS_SPONSORSHIP_QUERY_PARAM: &str = "disable_gas_sponsorship";
25/// The auth server query param for the gas refund address
26pub const GAS_REFUND_ADDRESS_QUERY_PARAM: &str = "refund_address";
27/// The auth server query param for refunding gas in terms of native ETH
28pub const GAS_REFUND_NATIVE_ETH_QUERY_PARAM: &str = "refund_native_eth";
29/// A builder for an [`ExternalOrderV2`]
30#[derive(Debug, Clone, Default)]
31pub struct ExternalOrderBuilderV2 {
32    /// The mint (erc20 address) of the input token
33    input_mint: Option<String>,
34    /// The mint (erc20 address) of the output token
35    output_mint: Option<String>,
36    /// The input amount of the order
37    input_amount: Option<Amount>,
38    /// The output amount of the order
39    output_amount: Option<Amount>,
40    /// Whether to consider the output amount as an exact amount (net of fees)
41    use_exact_output_amount: Option<bool>,
42    /// The minimum fill size
43    min_fill_size: Option<Amount>,
44}
45
46impl ExternalOrderBuilderV2 {
47    /// Create a new external order builder
48    pub fn new() -> Self {
49        Self::default()
50    }
51
52    /// Set the input mint
53    ///
54    /// Expects the input mint as a hex encoded string
55    pub fn input_mint(mut self, input_mint: &str) -> Self {
56        self.input_mint = Some(input_mint.to_string());
57        self
58    }
59
60    /// Set the output mint
61    pub fn output_mint(mut self, output_mint: &str) -> Self {
62        self.output_mint = Some(output_mint.to_string());
63        self
64    }
65
66    /// Set the input amount
67    pub fn input_amount(mut self, input_amount: Amount) -> Self {
68        self.input_amount = Some(input_amount);
69        self
70    }
71
72    /// Set the output amount
73    pub fn output_amount(mut self, output_amount: Amount) -> Self {
74        self.output_amount = Some(output_amount);
75        self
76    }
77
78    /// Set whether to use an exact output amount
79    pub fn use_exact_output_amount(mut self) -> Self {
80        self.use_exact_output_amount = Some(true);
81        self
82    }
83
84    /// Set the minimum fill size
85    pub fn min_fill_size(mut self, min_fill_size: Amount) -> Self {
86        self.min_fill_size = Some(min_fill_size);
87        self
88    }
89
90    /// Build the external order
91    pub fn build(self) -> Result<ExternalOrderV2, ExternalMatchClientError> {
92        let input_mint =
93            self.input_mint.ok_or(ExternalMatchClientError::invalid_order("invalid input mint"))?;
94
95        let output_mint = self
96            .output_mint
97            .ok_or(ExternalMatchClientError::invalid_order("invalid output mint"))?;
98
99        // Ensure exactly one of the amount fields is set
100        let input_zero = self.input_amount.is_none_or(|amt| amt == 0);
101        let output_zero = self.output_amount.is_none_or(|amt| amt == 0);
102        if !(input_zero ^ output_zero) {
103            return Err(ExternalMatchClientError::invalid_order(
104                "exactly one of input_amount or output_amount must be set",
105            ));
106        }
107
108        let input_amount = self.input_amount.unwrap_or_default();
109        let output_amount = self.output_amount.unwrap_or_default();
110        let use_exact_output_amount = self.use_exact_output_amount.unwrap_or_default();
111        let min_fill_size = self.min_fill_size.unwrap_or_default();
112
113        Ok(ExternalOrderV2 {
114            input_mint,
115            output_mint,
116            input_amount,
117            output_amount,
118            use_exact_output_amount,
119            min_fill_size,
120        })
121    }
122}
123
124/// A builder for an [`ExternalOrder`](v1_types::ExternalOrder) (v1 format)
125#[derive(Debug, Clone, Default)]
126pub struct ExternalOrderBuilder {
127    /// The mint (erc20 address) of the quote token
128    quote_mint: Option<String>,
129    /// The mint (erc20 address) of the base token
130    base_mint: Option<String>,
131    /// The base amount of the order
132    base_amount: Option<Amount>,
133    /// The quote amount of the order
134    quote_amount: Option<Amount>,
135    /// The exact base output amount
136    exact_base_output: Option<Amount>,
137    /// The exact quote output amount
138    exact_quote_output: Option<Amount>,
139    /// The side of the order
140    side: Option<OrderSide>,
141    /// The minimum fill size
142    min_fill_size: Option<Amount>,
143}
144
145impl ExternalOrderBuilder {
146    /// Create a new external order builder
147    pub fn new() -> Self {
148        Self::default()
149    }
150
151    /// Set the quote mint
152    ///
153    /// Expects the quote mint as a hex encoded string
154    pub fn quote_mint(mut self, quote_mint: &str) -> Self {
155        self.quote_mint = Some(quote_mint.to_string());
156        self
157    }
158
159    /// Set the base mint
160    pub fn base_mint(mut self, base_mint: &str) -> Self {
161        self.base_mint = Some(base_mint.to_string());
162        self
163    }
164
165    /// Set the base amount
166    pub fn base_amount(mut self, base_amount: Amount) -> Self {
167        self.base_amount = Some(base_amount);
168        self
169    }
170
171    /// Set the quote amount
172    pub fn quote_amount(mut self, quote_amount: Amount) -> Self {
173        self.quote_amount = Some(quote_amount);
174        self
175    }
176
177    /// Set the side
178    pub fn side(mut self, side: OrderSide) -> Self {
179        self.side = Some(side);
180        self
181    }
182
183    /// Set the exact base output amount
184    pub fn exact_base_output(mut self, exact_base_output: Amount) -> Self {
185        self.exact_base_output = Some(exact_base_output);
186        self
187    }
188
189    /// Set the exact quote output amount
190    pub fn exact_quote_output(mut self, exact_quote_output: Amount) -> Self {
191        self.exact_quote_output = Some(exact_quote_output);
192        self
193    }
194
195    /// Set the minimum fill size
196    pub fn min_fill_size(mut self, min_fill_size: Amount) -> Self {
197        self.min_fill_size = Some(min_fill_size);
198        self
199    }
200
201    /// Build the external order
202    pub fn build(self) -> Result<v1_types::ExternalOrder, ExternalMatchClientError> {
203        let quote_mint =
204            self.quote_mint.ok_or(ExternalMatchClientError::invalid_order("invalid quote mint"))?;
205        let base_mint =
206            self.base_mint.ok_or(ExternalMatchClientError::invalid_order("invalid base mint"))?;
207
208        // Ensure exactly one of the four amount fields is set
209        let base_zero = self.base_amount.is_none_or(|amt| amt == 0);
210        let quote_zero = self.quote_amount.is_none_or(|amt| amt == 0);
211        let exact_base_output = self.exact_base_output.is_some_and(|amt| amt != 0);
212        let exact_quote_output = self.exact_quote_output.is_some_and(|amt| amt != 0);
213
214        let n_sizes_set = (!base_zero as u8)
215            + (!quote_zero as u8)
216            + (exact_base_output as u8)
217            + (exact_quote_output as u8);
218
219        if n_sizes_set != 1 {
220            return Err(ExternalMatchClientError::invalid_order(
221                "exactly one of base_amount, quote_amount, exact_base_output, or exact_quote_output must be set",
222            ));
223        }
224
225        let base_amount = self.base_amount.unwrap_or_default();
226        let quote_amount = self.quote_amount.unwrap_or_default();
227        let exact_base_output = self.exact_base_output.unwrap_or_default();
228        let exact_quote_output = self.exact_quote_output.unwrap_or_default();
229
230        let side = self.side.ok_or(ExternalMatchClientError::invalid_order("invalid side"))?;
231        let min_fill_size = self.min_fill_size.unwrap_or_default();
232
233        Ok(v1_types::ExternalOrder {
234            quote_mint,
235            base_mint,
236            side,
237            base_amount,
238            quote_amount,
239            exact_base_output,
240            exact_quote_output,
241            min_fill_size,
242        })
243    }
244}