Skip to main content

jup_ag_sdk/types/
recurring.rs

1use super::OrderStatus;
2use serde::{Deserialize, Serialize};
3
4/// Represents a request to create a recurring order, either time-based or price-based.
5#[derive(Serialize, Deserialize, Debug)]
6#[serde(rename_all = "camelCase")]
7pub struct CreateRecurringOrderRequest {
8    /// The wallet address initiating the order.
9    pub user: String,
10    /// The mint address of the input SPL token.
11    pub input_mint: String,
12    /// The mint address of the output SPL token.
13    pub output_mint: String,
14    /// Parameters for the recurring order, either time-based or price-based.
15    pub params: OrderParams,
16}
17
18/// Enum wrapper for the two types of recurring order strategies:
19/// - `TimeWrapper`: Splits funds over time.
20/// - `PriceWrapper`: Splits funds based on price increments.
21#[derive(Serialize, Deserialize, Debug)]
22#[serde(untagged)]
23pub enum OrderParams {
24    /// Time-based recurring order parameters.
25    TimeWrapper { time: TimeParams },
26    /// Price-based recurring order parameters.
27    PriceWrapper { price: PriceParams },
28}
29
30/// Parameters for creating a time-based recurring order.
31#[derive(Serialize, Deserialize, Debug)]
32#[serde(rename_all = "camelCase")]
33pub struct TimeParams {
34    /// Total input token amount to be split across orders.
35    pub in_amount: u64,
36    /// Number of orders to place.
37    pub number_of_orders: u64,
38    /// Time interval (in seconds) between each order.
39    pub interval: u64,
40    /// Optional minimum price threshold for executing an order.
41    pub min_price: Option<f64>,
42    /// Optional maximum price threshold for executing an order.
43    pub max_price: Option<f64>,
44    /// Optional Unix timestamp to schedule when the order starts.
45    pub start_at: Option<u64>,
46}
47
48/// Parameters for creating a price-based recurring order.
49#[derive(Serialize, Deserialize, Debug)]
50#[serde(rename_all = "camelCase")]
51pub struct PriceParams {
52    /// Total amount to be deposited for the strategy.
53    pub deposit_amount: u64,
54    /// USDC value increment that triggers each order.
55    pub increment_usdc_value: u64,
56    /// Minimum time interval (in seconds) between each order execution.
57    pub interval: u64,
58    /// Optional Unix timestamp to schedule when the order starts.
59    pub start_at: Option<u64>,
60}
61
62impl CreateRecurringOrderRequest {
63    /// Creates a new time-based recurring order.
64    ///
65    /// # Arguments
66    ///
67    /// * `user` - The user wallet address.
68    /// * `input_mint` - SPL token mint address for the input token.
69    /// * `output_mint` - SPL token mint address for the output token.
70    /// * `in_amount` - Total input amount to be divided.
71    /// * `number_of_orders` - How many times the order should be placed.
72    /// * `interval` - Time gap between each order (in seconds).
73    pub fn new_time_order(
74        user: impl Into<String>,
75        input_mint: impl Into<String>,
76        output_mint: impl Into<String>,
77        in_amount: u64,
78        number_of_orders: u64,
79        interval: u64,
80    ) -> Self {
81        let params = TimeParams {
82            in_amount,
83            number_of_orders,
84            interval,
85            min_price: None,
86            max_price: None,
87            start_at: None,
88        };
89        Self {
90            user: user.into(),
91            input_mint: input_mint.into(),
92            output_mint: output_mint.into(),
93            params: OrderParams::TimeWrapper { time: params },
94        }
95    }
96
97    /// Creates a new price-based recurring order.
98    ///
99    /// # Arguments
100    ///
101    /// * `user` - The user wallet address.
102    /// * `input_mint` - SPL token mint address for the input token.
103    /// * `output_mint` - SPL token mint address for the output token.
104    /// * `deposit_amount` - Total amount to deposit.
105    /// * `increment_usdc_value` - Price increment in USDC for each order trigger.
106    /// * `interval` - Minimum interval between executions (in seconds).
107    pub fn new_price_order(
108        user: impl Into<String>,
109        input_mint: impl Into<String>,
110        output_mint: impl Into<String>,
111        deposit_amount: u64,
112        increment_usdc_value: u64,
113        interval: u64,
114    ) -> Self {
115        let params = PriceParams {
116            deposit_amount,
117            increment_usdc_value,
118            interval,
119            start_at: None,
120        };
121
122        Self {
123            user: user.into(),
124            input_mint: input_mint.into(),
125            output_mint: output_mint.into(),
126            params: OrderParams::PriceWrapper { price: params },
127        }
128    }
129
130    /// Sets the `start_at` Unix timestamp to delay the start of the recurring order.
131    pub fn with_start_at(mut self, start_at: u64) -> Self {
132        match &mut self.params {
133            OrderParams::TimeWrapper { time } => time.start_at = Some(start_at),
134            OrderParams::PriceWrapper { price } => price.start_at = Some(start_at),
135        }
136        self
137    }
138
139    /// Sets the optional `min_price` threshold for a time-based order.
140    pub fn with_min_price(mut self, price: f64) -> Self {
141        if let OrderParams::TimeWrapper { time } = &mut self.params {
142            time.min_price = Some(price);
143        }
144        self
145    }
146
147    /// Sets the optional `max_price` threshold for a time-based order.
148    pub fn with_max_price(mut self, price: f64) -> Self {
149        if let OrderParams::TimeWrapper { time } = &mut self.params {
150            time.max_price = Some(price);
151        }
152        self
153    }
154}
155
156#[derive(Debug, Serialize, Deserialize)]
157#[serde(rename_all = "camelCase")]
158pub struct CancelRecurringOrderRequest {
159    pub order: String,
160
161    pub recurring_type: RecurringOrderType,
162
163    pub user: String,
164}
165
166impl CancelRecurringOrderRequest {
167    pub fn new(
168        order: impl Into<String>,
169        recurring_type: RecurringOrderType,
170        user: impl Into<String>,
171    ) -> Self {
172        Self {
173            order: order.into(),
174            recurring_type,
175            user: user.into(),
176        }
177    }
178}
179
180#[derive(Debug, Serialize, Deserialize)]
181#[serde(rename_all = "lowercase")]
182pub enum RecurringOrderType {
183    Time,
184    Price,
185    /// All type is to only be used to get all recurring orders not a actual order type
186    All,
187}
188
189#[derive(Debug, Serialize, Deserialize)]
190pub struct PriceDeposit {
191    pub amount: u64,
192
193    pub order: String,
194
195    pub user: String,
196}
197
198impl PriceDeposit {
199    /// # Arguments
200    ///
201    /// * `amount` - The amount to deposit
202    /// * `order` - The recurring order account address
203    /// * `user` - The user account address
204    pub fn new(amount: u64, order: impl Into<String>, user: impl Into<String>) -> Self {
205        Self {
206            amount,
207            order: order.into(),
208            user: user.into(),
209        }
210    }
211}
212
213#[derive(Debug, Serialize, Deserialize)]
214#[serde(rename_all = "camelCase")]
215pub struct PriceWithdraw {
216    /// If no amount is provided, it will withdraw the entire amount
217    pub amount: u64,
218
219    pub order: String,
220
221    pub user: String,
222
223    /// Possible values: [In, Out]
224    pub input_or_output: String,
225}
226
227impl PriceWithdraw {
228    /// # Arguments
229    ///
230    /// * `amount` - The amount to withdraw
231    /// * `order` - The recurring order account address
232    /// * `user` - The user account address
233    /// * `input_or_output` - The withdrawal direction ("In" or "Out")
234    pub fn new(
235        amount: u64,
236        order: impl Into<String>,
237        user: impl Into<String>,
238        input_or_output: impl Into<String>,
239    ) -> Self {
240        Self {
241            amount,
242            order: order.into(),
243            user: user.into(),
244            input_or_output: input_or_output.into(),
245        }
246    }
247}
248
249#[derive(Debug, Serialize, Deserialize)]
250#[serde(rename_all = "camelCase")]
251pub struct RecurringResponse {
252    pub request_id: String,
253
254    /// Unsigned base-64 encoded transaction
255    pub transaction: String,
256}
257
258#[derive(Debug, Serialize, Deserialize)]
259#[serde(rename_all = "camelCase")]
260pub struct ExecuteRecurringRequest {
261    pub request_id: String,
262
263    pub signed_transaction: String,
264}
265
266impl ExecuteRecurringRequest {
267    pub fn new(request_id: impl Into<String>, signed_transaction: impl Into<String>) -> Self {
268        Self {
269            request_id: request_id.into(),
270            signed_transaction: signed_transaction.into(),
271        }
272    }
273}
274
275#[derive(Debug, Serialize, Deserialize)]
276#[serde(rename_all = "camelCase")]
277pub struct ExecuteRecurringResponse {
278    pub signature: String,
279
280    pub status: String,
281}
282
283#[derive(Debug, Serialize, Deserialize)]
284#[serde(rename_all = "camelCase")]
285pub struct GetRecurringOrders {
286    pub recurring_type: RecurringOrderType,
287    pub order_status: OrderStatus,
288    pub user: String,
289    pub page: u64,
290    pub mint: Option<String>,
291    pub include_failed_tx: bool,
292}
293
294impl GetRecurringOrders {
295    /// Basic constructor
296    pub fn new(
297        recurring_type: RecurringOrderType,
298        order_status: OrderStatus,
299        user: impl Into<String>,
300    ) -> Self {
301        Self {
302            recurring_type,
303            order_status,
304            user: user.into(),
305            page: 1,
306            mint: None,
307            include_failed_tx: false,
308        }
309    }
310
311    /// Customize page number
312    pub fn with_page(mut self, page: u64) -> Self {
313        self.page = page;
314        self
315    }
316
317    /// Filter by a specific mint
318    pub fn with_mint(mut self, mint: impl Into<String>) -> Self {
319        self.mint = Some(mint.into());
320        self
321    }
322
323    /// Include failed transactions
324    pub fn include_failed(mut self) -> Self {
325        self.include_failed_tx = true;
326        self
327    }
328}
329
330#[derive(Debug, Serialize, Deserialize)]
331#[serde(rename_all = "camelCase")]
332pub struct RecurringOrders {
333    pub order_status: OrderStatus,
334    pub page: u64,
335    pub total_pages: u64,
336    pub user: String,
337    #[serde(default)]
338    pub time: Option<Vec<TimeOrder>>,
339    #[serde(default)]
340    pub price: Option<Vec<PriceOrder>>,
341    #[serde(default)]
342    pub all: Option<Vec<Order>>,
343}
344
345#[derive(Debug, Serialize, Deserialize)]
346#[serde(untagged)]
347pub enum Order {
348    Time(TimeOrder),
349    Price(PriceOrder),
350}
351
352#[derive(Debug, Serialize, Deserialize)]
353#[serde(rename_all = "camelCase")]
354pub struct PriceOrder {
355    pub close_tx: String,
356    pub closed_by: String,
357    pub created_at: String,
358    pub estimated_usdc_value_spent: String,
359    pub in_deposited: String,
360    pub in_left: String,
361    pub in_used: String,
362    pub in_withdrawn: String,
363    pub incremental_usd_value: String,
364    pub input_mint: String,
365    pub open_tx: String,
366    pub order_interval: String,
367    pub order_key: String,
368    pub out_received: String,
369    pub out_withdrawn: String,
370    pub output_mint: String,
371    pub raw_estimated_usdc_value_spent: String,
372    pub raw_in_deposited: String,
373    pub raw_in_left: String,
374    pub raw_in_used: String,
375    pub raw_in_withdrawn: String,
376    pub raw_incremental_usd_value: String,
377    pub raw_out_received: String,
378    pub raw_out_withdrawn: String,
379    pub raw_supposed_usd_value: String,
380    pub start_at: String,
381    pub status: String,
382    pub supposed_usd_value: String,
383    pub trades: Vec<Trade>,
384    pub updated_at: String,
385    pub user_pubkey: String,
386}
387
388#[derive(Debug, Serialize, Deserialize)]
389#[serde(rename_all = "camelCase")]
390pub struct TimeOrder {
391    pub close_tx: String,
392    pub created_at: String,
393    pub cycle_frequency: String,
394    pub in_amount_per_cycle: String,
395    pub in_deposited: String,
396    pub in_used: String,
397    pub in_withdrawn: String,
398    pub input_mint: String,
399    pub max_out_amount: String,
400    pub min_out_amount: String,
401    pub open_tx: String,
402    pub order_key: String,
403    pub out_received: String,
404    pub out_withdrawn: String,
405    pub output_mint: String,
406    pub raw_in_amount_per_cycle: String,
407    pub raw_in_deposited: String,
408    pub raw_in_used: String,
409    pub raw_in_withdrawn: String,
410    pub raw_max_out_amount: String,
411    pub raw_min_out_amount: String,
412    pub raw_out_received: String,
413    pub raw_out_withdrawn: String,
414    pub trades: Vec<Trade>,
415    pub updated_at: String,
416    pub user_closed: bool,
417    pub user_pubkey: String,
418}
419
420#[derive(Debug, Serialize, Deserialize)]
421#[serde(rename_all = "camelCase")]
422pub struct Trade {
423    pub action: String,
424    pub confirmed_at: String,
425    pub fee_amount: String,
426    pub fee_mint: String,
427    pub input_amount: String,
428    pub input_mint: String,
429    pub keeper: String,
430    pub order_key: String,
431    pub output_amount: String,
432    pub output_mint: String,
433    #[serde(default)]
434    pub product_meta: Option<ProductMeta>,
435    pub raw_fee_amount: String,
436    pub raw_input_amount: String,
437    pub raw_output_amount: String,
438    pub tx_id: String,
439}
440
441#[derive(Debug, Serialize, Deserialize)]
442#[serde(rename_all = "snake_case")]
443pub struct ProductMeta {
444    pub new_actual_usdc_value: String,
445    pub value: String,
446}