levana_perpswap_cosmos/contracts/
copy_trading.rs

1//! Copy trading contract
2
3use std::{fmt::Display, num::ParseIntError, str::FromStr};
4
5use super::market::{
6    entry::{ExecuteMsg as MarketExecuteMsg, SlippageAssert, StopLoss},
7    order::OrderId,
8    position::PositionId,
9};
10use crate::{
11    number::{Collateral, LpToken, NonZero},
12    price::{PriceBaseInQuote, PricePoint, TakeProfitTrader},
13    storage::{DirectionToBase, LeverageToBase, MarketId, RawAddr},
14    time::Timestamp,
15};
16use anyhow::{anyhow, bail};
17use cosmwasm_std::{Addr, Binary, Decimal256, StdError, StdResult, Uint128, Uint64};
18use cw_storage_plus::{IntKey, Key, KeyDeserialize, Prefixer, PrimaryKey};
19use thiserror::Error;
20
21/// Message for instantiating a new copy trading contract.
22#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
23#[serde(rename_all = "snake_case")]
24pub struct InstantiateMsg {
25    /// Leader of the contract
26    pub leader: RawAddr,
27    /// Initial configuration values
28    pub config: ConfigUpdate,
29    /// Initial parameters
30    pub parameters: FactoryConfigUpdate,
31}
32
33/// Full configuration
34#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
35#[serde(rename_all = "snake_case")]
36/// Updates to configuration values.
37pub struct Config {
38    /// Factory we will allow trading on.
39    pub factory: Addr,
40    /// Administrator of the contract. Should be the factory contract
41    /// which initializes this.
42    pub admin: Addr,
43    /// Pending administrator, ready to be accepted, if any.
44    pub pending_admin: Option<Addr>,
45    /// Leader of the contract
46    pub leader: Addr,
47    /// Name given to this copy_trading pool
48    pub name: String,
49    /// Description of the copy_trading pool. Not more than 128
50    /// characters.
51    pub description: String,
52    /// Commission rate for the leader. Must be within 1-30%.
53    pub commission_rate: Decimal256,
54    /// Creation time of contract
55    pub created_at: Timestamp,
56    /// Allowed smart queries during Rebalance operation
57    pub allowed_rebalance_queries: u32,
58    /// Allowed smart queries during token value computation
59    pub allowed_lp_token_queries: u32,
60}
61
62impl Config {
63    /// Check validity of config values
64    pub fn check(&self) -> anyhow::Result<()> {
65        if self.name.len() > 128 {
66            Err(anyhow!(
67                "Description should not be more than 128 characters"
68            ))
69        } else if self.commission_rate < Decimal256::from_ratio(1u32, 100u32) {
70            Err(anyhow!("Commission rate less than 1 percent"))
71        } else if self.commission_rate > Decimal256::from_ratio(30u32, 100u32) {
72            Err(anyhow!("Commission rate greater than 30 percent"))
73        } else {
74            Ok(())
75        }
76    }
77
78    /// Check leader
79    pub fn ensure_leader(&self, sender: &Addr) -> anyhow::Result<()> {
80        if self.leader != sender {
81            bail!("Unautorized access, only {} allowed", self.leader)
82        }
83        Ok(())
84    }
85
86    /// Ensure it is factory contract
87    pub fn ensure_factory(&self, sender: &Addr) -> anyhow::Result<()> {
88        if self.factory != sender {
89            bail!("Unautorized access, only {} allowed", self.factory)
90        }
91        Ok(())
92    }
93}
94
95/// Updates to configuration values.
96///
97/// See [Config] for field meanings.
98#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
99#[serde(rename_all = "snake_case")]
100#[allow(missing_docs)]
101pub struct ConfigUpdate {
102    pub name: Option<String>,
103    pub description: Option<String>,
104    pub commission_rate: Option<Decimal256>,
105}
106
107/// Updates to configuration values.
108///
109/// See [Config] for field meanings.
110#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
111#[serde(rename_all = "snake_case")]
112#[allow(missing_docs)]
113pub struct FactoryConfigUpdate {
114    pub allowed_rebalance_queries: Option<u32>,
115    pub allowed_lp_token_queries: Option<u32>,
116}
117
118/// Executions available on the copy trading contract.
119#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
120#[serde(rename_all = "snake_case")]
121pub enum ExecuteMsg {
122    /// Cw20 interface
123    Receive {
124        /// Owner of funds sent to the contract
125        sender: RawAddr,
126        /// Amount of funds sent
127        amount: Uint128,
128        /// Must parse to a [ExecuteMsg]
129        msg: Binary,
130    },
131    /// Deposit funds to the contract
132    Deposit {},
133    /// Withdraw funds from a given market
134    Withdraw {
135        /// The number of LP shares to remove
136        shares: NonZero<LpToken>,
137        /// Token type in which amount should be withdrawn
138        token: Token,
139    },
140    /// Update configuration values that is allowed for leader.
141    LeaderUpdateConfig(ConfigUpdate),
142    /// Update configuration values that is allowed for facotr.
143    FactoryUpdateConfig(FactoryConfigUpdate),
144    /// Leader specific execute message for the market
145    LeaderMsg {
146        /// Market id that message is for
147        market_id: MarketId,
148        /// Message
149        message: Box<MarketExecuteMsg>,
150        /// Collateral to use for the action
151        collateral: Option<NonZero<Collateral>>,
152    },
153    /// Leader withdrawal (for commission).
154    LeaderWithdrawal {
155        /// Fund to be withdrawn
156        requested_funds: NonZero<Collateral>,
157        /// Token type in which amount should be withdrawn
158        token: Token,
159    },
160    /// Perform queue work
161    DoWork {},
162}
163
164/// Queries that can be performed on the copy contract.
165#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
166#[serde(rename_all = "snake_case")]
167pub enum QueryMsg {
168    /// Get the current config
169    ///
170    /// Returns [Config]
171    Config {},
172    /// Get the queue status of a particular wallet
173    ///
174    /// Returns [QueueResp]
175    QueueStatus {
176        /// Address of the wallet
177        address: RawAddr,
178        /// Value from [QueueResp]
179        start_after: Option<QueuePositionId>,
180        /// How many values to return
181        limit: Option<u32>,
182    },
183    /// Returns the share held by the wallet
184    ///
185    /// Returns [BalanceResp]
186    Balance {
187        /// Address of the token holder
188        address: RawAddr,
189        /// Value from [BalanceResp]
190        start_after: Option<Token>,
191        /// How many values to return
192        limit: Option<u32>,
193    },
194    /// Check the status of the copy trading contract for all the
195    /// markets that it's trading on
196    ///
197    /// Returns [LeaderStatusResp]
198    LeaderStatus {
199        /// Value from [TokenStatus::token]
200        start_after: Option<Token>,
201        /// How many values to return
202        limit: Option<u32>,
203    },
204    /// Does it have any pending work ?
205    ///
206    /// Returns [WorkResp]
207    HasWork {},
208}
209
210/// Individual response from [QueryMsg::QueueStatus]
211#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
212#[serde(rename_all = "snake_case")]
213pub struct QueueResp {
214    /// Items in queue for the wallet
215    pub items: Vec<QueueItemStatus>,
216    /// Last processed [QueuePositionId]
217    pub inc_processed_till: Option<IncQueuePositionId>,
218    /// Last processed [QueuePositionId]
219    pub dec_processed_till: Option<DecQueuePositionId>,
220}
221
222/// Queue item status
223#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
224#[serde(rename_all = "snake_case")]
225pub struct QueueItemStatus {
226    /// Queue item
227    pub item: QueueItem,
228    /// Status of processing
229    pub status: ProcessingStatus,
230}
231
232/// Queue item Processing status
233#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
234#[serde(rename_all = "snake_case")]
235pub enum ProcessingStatus {
236    /// Not started processing yet
237    NotProcessed,
238    /// Successfully finished processing
239    Finished,
240    /// In progress
241    InProgress,
242    /// Failed during processing
243    Failed(FailedReason),
244}
245
246impl ProcessingStatus {
247    /// Did the processing fail ?
248    pub fn failed(&self) -> bool {
249        match self {
250            ProcessingStatus::NotProcessed => false,
251            ProcessingStatus::Finished => false,
252            ProcessingStatus::Failed(_) => true,
253            ProcessingStatus::InProgress => false,
254        }
255    }
256
257    /// Is any status pending ?
258    pub fn pending(&self) -> bool {
259        match self {
260            ProcessingStatus::NotProcessed => true,
261            ProcessingStatus::Finished => false,
262            ProcessingStatus::Failed(_) => false,
263            ProcessingStatus::InProgress => true,
264        }
265    }
266
267    /// Did the status finish ?
268    pub fn finish(&self) -> bool {
269        match self {
270            ProcessingStatus::NotProcessed => false,
271            ProcessingStatus::Finished => true,
272            ProcessingStatus::Failed(_) => false,
273            ProcessingStatus::InProgress => false,
274        }
275    }
276
277    /// Is any status currently in progres ?
278    pub fn in_progress(&self) -> bool {
279        match self {
280            ProcessingStatus::NotProcessed => false,
281            ProcessingStatus::Finished => false,
282            ProcessingStatus::Failed(_) => false,
283            ProcessingStatus::InProgress => true,
284        }
285    }
286}
287
288/// Failure reason on why queue processing failed
289#[derive(Error, Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
290#[serde(rename_all = "snake_case")]
291pub enum FailedReason {
292    /// Not enough collateral available
293    #[error("Collateral not available. Requested {requested}, but only available {available}")]
294    NotEnoughCollateral {
295        /// Available collateral
296        available: Collateral,
297        /// Requested collateral
298        requested: NonZero<Collateral>,
299    },
300    /// Not enough crank fee
301    #[error("Crank fee not available. Requested {requested}, but only available {available}")]
302    NotEnoughCrankFee {
303        /// Available collateral
304        available: Collateral,
305        /// Requested collateral
306        requested: Collateral,
307    },
308    /// Fund less than chain's minimum representation
309    #[error("Collateral amount {funds} is less than chain's minimum representation.not available")]
310    FundLessThanMinChain {
311        /// Requested collateral
312        funds: NonZero<Collateral>,
313    },
314    /// Wallet does not have enough shares
315    #[error("Shares not available. Requested {requested}, but only available {available}")]
316    NotEnoughShares {
317        /// Available shares
318        available: LpToken,
319        /// Requested shares
320        requested: LpToken,
321    },
322    /// Received error from Market contract
323    #[error("{market_id} result in error: {message}")]
324    MarketError {
325        /// Market ID which result in error
326        market_id: MarketId,
327        /// Error message
328        message: String,
329    },
330    /// Deferred exec failure
331    #[error("Deferred exec failure at {executed} because of: {reason}")]
332    DeferredExecFailure {
333        /// Reason it didn't apply successfully
334        reason: String,
335        /// Timestamp when it failed execution
336        executed: Timestamp,
337        /// Price point when it was cranked, if applicable
338        crank_price: Option<PricePoint>,
339    },
340}
341
342/// Queue Item
343#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
344#[serde(rename_all = "snake_case")]
345pub enum QueueItem {
346    /// Item that will lead to increase or no change of collateral
347    IncCollateral {
348        /// Item type
349        item: IncQueueItem,
350        /// Queue position id
351        id: IncQueuePositionId,
352    },
353    /// Item that will lead to decrease of collateral
354    DecCollateral {
355        /// Item type
356        item: Box<DecQueueItem>,
357        /// Queue position id
358        id: DecQueuePositionId,
359    },
360}
361
362/// Queue item that needs to be processed
363#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
364#[serde(rename_all = "snake_case")]
365pub enum IncQueueItem {
366    /// Deposit the fund and get some [LpToken]
367    Deposit {
368        /// Funds to be deposited
369        funds: NonZero<Collateral>,
370        /// Token
371        token: Token,
372    },
373    /// Market action items
374    MarketItem {
375        /// Market id
376        id: MarketId,
377        /// Market token
378        token: Token,
379        /// Market item
380        item: Box<IncMarketItem>,
381    },
382}
383
384/// Queue item that needs to be processed
385#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
386#[serde(rename_all = "snake_case")]
387pub enum DecQueueItem {
388    /// Withdraw via LpToken
389    Withdrawal {
390        /// Tokens to be withdrawn
391        tokens: NonZero<LpToken>,
392        /// Token type
393        token: Token,
394    },
395    /// Market action items
396    MarketItem {
397        /// Market id
398        id: MarketId,
399        /// Market token
400        token: Token,
401        /// Market item
402        item: Box<DecMarketItem>,
403    },
404}
405
406/// Queue item that needs to be processed
407#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
408#[serde(rename_all = "snake_case")]
409pub enum IncMarketItem {
410    /// Remove collateral from a position, causing leverage to increase
411    UpdatePositionRemoveCollateralImpactLeverage {
412        /// ID of position to update
413        id: PositionId,
414        /// Amount of funds to remove from the position
415        amount: NonZero<Collateral>,
416    },
417    /// Remove collateral from a position, causing leverage to increase
418    UpdatePositionRemoveCollateralImpactSize {
419        /// ID of position to update
420        id: PositionId,
421        /// Amount of funds to remove from the position
422        amount: NonZero<Collateral>,
423        /// Slippage alert
424        slippage_assert: Option<SlippageAssert>,
425    },
426    /// Cancel an open limit order
427    CancelLimitOrder {
428        /// ID of the order
429        order_id: OrderId,
430    },
431    /// Close a position
432    ClosePosition {
433        /// ID of position to close
434        id: PositionId,
435        /// Assertion that the price has not moved too far
436        slippage_assert: Option<SlippageAssert>,
437    },
438}
439
440/// Queue item that needs to be processed
441#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
442#[serde(rename_all = "snake_case")]
443pub enum DecMarketItem {
444    /// Open position
445    OpenPosition {
446        /// Collateral for the position
447        collateral: NonZero<Collateral>,
448        /// Assertion that the price has not moved too far
449        slippage_assert: Option<SlippageAssert>,
450        /// Leverage of new position
451        leverage: LeverageToBase,
452        /// Direction of new position
453        direction: DirectionToBase,
454        /// Stop loss price of new position
455        stop_loss_override: Option<PriceBaseInQuote>,
456        /// Take profit price of new position
457        #[serde(alias = "take_profit_override")]
458        take_profit: TakeProfitTrader,
459    },
460    /// Add collateral to position, causing leverage to decrease
461    UpdatePositionAddCollateralImpactLeverage {
462        /// Collateral that will be added
463        collateral: NonZero<Collateral>,
464        /// ID of position to update
465        id: PositionId,
466    },
467    /// Add collateral to position, causing notional size to increase
468    UpdatePositionAddCollateralImpactSize {
469        /// Collateral that will be added
470        collateral: NonZero<Collateral>,
471        /// ID of position to update
472        id: PositionId,
473        /// Slippage assert
474        slippage_assert: Option<SlippageAssert>,
475    },
476    /// Update position leverage
477    UpdatePositionLeverage {
478        /// ID of position to update
479        id: PositionId,
480        /// New leverage of the position
481        leverage: LeverageToBase,
482        /// Slippage assert
483        slippage_assert: Option<SlippageAssert>,
484    },
485    /// Modify the take profit price of a position
486    UpdatePositionTakeProfitPrice {
487        /// ID of position to update
488        id: PositionId,
489        /// New take profit price of the position
490        price: TakeProfitTrader,
491    },
492    /// Update the stop loss price of a position
493    UpdatePositionStopLossPrice {
494        /// ID of position to update
495        id: PositionId,
496        /// New stop loss price of the position, or remove
497        stop_loss: StopLoss,
498    },
499    /// Set a limit order to open a position when the price of the asset hits
500    /// the specified trigger price.
501    PlaceLimitOrder {
502        /// Collateral for the position
503        collateral: NonZero<Collateral>,
504        /// Price when the order should trigger
505        trigger_price: PriceBaseInQuote,
506        /// Leverage of new position
507        leverage: LeverageToBase,
508        /// Direction of new position
509        direction: DirectionToBase,
510        /// Stop loss price of new position
511        stop_loss_override: Option<PriceBaseInQuote>,
512        /// Take profit price of new position
513        #[serde(alias = "take_profit_override")]
514        take_profit: TakeProfitTrader,
515    },
516}
517
518/// Token required for the queue item
519pub enum RequiresToken {
520    /// Token required for LP token value computation
521    Token {
522        /// Token
523        token: Token,
524    },
525    /// Token not requird
526    NoToken {},
527}
528
529impl IncQueueItem {
530    /// Does this queue item require computation of LP token value
531    pub fn requires_token(self) -> RequiresToken {
532        match self {
533            IncQueueItem::Deposit { token, .. } => RequiresToken::Token { token },
534            IncQueueItem::MarketItem { .. } => RequiresToken::NoToken {},
535        }
536    }
537}
538
539impl DecQueueItem {
540    /// Does this queue item require computation of LP token value
541    pub fn requires_token(self) -> RequiresToken {
542        match self {
543            DecQueueItem::Withdrawal { token, .. } => RequiresToken::Token { token },
544            // Market item does not require computation of lp token value
545            DecQueueItem::MarketItem { .. } => RequiresToken::NoToken {},
546        }
547    }
548}
549
550/// Individual response from [QueryMsg::LeaderStatus]
551#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
552#[serde(rename_all = "snake_case")]
553pub struct LeaderStatusResp {
554    /// Tokens for the leader
555    pub tokens: Vec<TokenStatus>,
556}
557
558/// Individual response from [QueryMsg::LeaderStatus]
559#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
560#[serde(rename_all = "snake_case")]
561pub struct TokenStatus {
562    /// Token
563    pub token: Token,
564    /// Available collateral for the leader
565    pub collateral: Collateral,
566    /// Total shares so far. Represents AUM.
567    pub shares: LpToken,
568    /// Unclaimed commission
569    pub unclaimed_commission: Collateral,
570    /// Claimed commission
571    pub claimed_commission: Collateral,
572}
573
574/// Individual market response from [QueryMsg::Balance]
575#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
576#[serde(rename_all = "snake_case")]
577pub struct BalanceResp {
578    /// Shares of the pool held by the wallet
579    pub balance: Vec<BalanceRespItem>,
580    /// Start after that should be passed for next iteration
581    pub start_after: Option<Token>,
582}
583
584/// Individual market response inside [BalanceResp]
585#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
586#[serde(rename_all = "snake_case")]
587pub struct BalanceRespItem {
588    /// Shares of the pool held by the wallet
589    pub shares: NonZero<LpToken>,
590    /// Token type
591    pub token: Token,
592}
593
594/// Token accepted by the contract
595#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
596#[serde(rename_all = "snake_case")]
597pub enum Token {
598    /// Native coin and its denom
599    Native(String),
600    /// CW20 contract and its address
601    Cw20(Addr),
602}
603
604impl Token {
605    /// Is it same as market token ?
606    pub fn is_same(&self, token: &crate::token::Token) -> bool {
607        match token {
608            crate::token::Token::Cw20 { addr, .. } => match self {
609                Token::Native(_) => false,
610                Token::Cw20(cw20_addr) => {
611                    let cw20_addr: &RawAddr = &cw20_addr.into();
612                    cw20_addr == addr
613                }
614            },
615            crate::token::Token::Native { denom, .. } => match self {
616                Token::Native(native_denom) => *native_denom == *denom,
617                Token::Cw20(_) => false,
618            },
619        }
620    }
621}
622
623impl<'a> PrimaryKey<'a> for Token {
624    type Prefix = ();
625    type SubPrefix = ();
626    type Suffix = Self;
627    type SuperSuffix = Self;
628
629    fn key(&self) -> Vec<Key> {
630        let (token_type, bytes) = match self {
631            Token::Native(native) => (0u8, native.as_bytes()),
632            Token::Cw20(cw20) => (1u8, cw20.as_bytes()),
633        };
634        let token_type = Key::Val8([token_type]);
635        let key = Key::Ref(bytes);
636
637        vec![token_type, key]
638    }
639}
640
641impl<'a> Prefixer<'a> for Token {
642    fn prefix(&self) -> Vec<Key> {
643        let (token_type, bytes) = match self {
644            Token::Native(native) => (0u8, native.as_bytes()),
645            Token::Cw20(cw20) => (1u8, cw20.as_bytes()),
646        };
647        let token_type = Key::Val8([token_type]);
648        let key = Key::Ref(bytes);
649        vec![token_type, key]
650    }
651}
652
653impl KeyDeserialize for Token {
654    type Output = Token;
655
656    const KEY_ELEMS: u16 = 2;
657
658    fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
659        let (token_type, token) = <(u8, Vec<u8>) as KeyDeserialize>::from_vec(value)?;
660        let token = match token_type {
661            0 => {
662                let native_token = String::from_slice(&token)?;
663                Token::Native(native_token)
664            }
665            1 => {
666                let cw20_token = Addr::from_slice(&token)?;
667                Token::Cw20(cw20_token)
668            }
669            _ => {
670                return Err(StdError::serialize_err(
671                    "Token",
672                    "Invalid number in token_type",
673                ))
674            }
675        };
676        Ok(token)
677    }
678}
679
680impl KeyDeserialize for &Token {
681    type Output = Token;
682
683    const KEY_ELEMS: u16 = 2;
684
685    fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
686        let token = <Token as KeyDeserialize>::from_vec(value)?;
687        Ok(token)
688    }
689}
690
691impl Display for Token {
692    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
693        match self {
694            Token::Native(denom) => f.write_str(denom),
695            Token::Cw20(addr) => f.write_str(addr.as_str()),
696        }
697    }
698}
699
700impl Token {
701    /// Ensure that the two versions of the token are compatible.
702    pub fn ensure_matches(&self, token: &crate::token::Token) -> anyhow::Result<()> {
703        match (self, token) {
704            (Token::Native(_), crate::token::Token::Cw20 { addr, .. }) => {
705                anyhow::bail!("Provided native funds, but market requires a CW20 (contract {addr})")
706            }
707            (
708                Token::Native(denom1),
709                crate::token::Token::Native {
710                    denom: denom2,
711                    decimal_places: _,
712                },
713            ) => {
714                if denom1 == denom2 {
715                    Ok(())
716                } else {
717                    Err(anyhow::anyhow!("Wrong denom provided. You sent {denom1}, but the contract expects {denom2}"))
718                }
719            }
720            (
721                Token::Cw20(addr1),
722                crate::token::Token::Cw20 {
723                    addr: addr2,
724                    decimal_places: _,
725                },
726            ) => {
727                if addr1.as_str() == addr2.as_str() {
728                    Ok(())
729                } else {
730                    Err(anyhow::anyhow!(
731                        "Wrong CW20 used. You used {addr1}, but the contract expects {addr2}"
732                    ))
733                }
734            }
735            (Token::Cw20(_), crate::token::Token::Native { denom, .. }) => {
736                anyhow::bail!(
737                    "Provided CW20 funds, but market requires native funds with denom {denom}"
738                )
739            }
740        }
741    }
742}
743
744/// Work response
745#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
746#[serde(rename_all = "snake_case")]
747pub enum WorkResp {
748    /// No work found
749    NoWork,
750    /// Has some work
751    HasWork {
752        /// Work description
753        work_description: WorkDescription,
754    },
755}
756
757impl WorkResp {
758    /// Does it have work ?
759    pub fn has_work(&self) -> bool {
760        match self {
761            WorkResp::NoWork => false,
762            WorkResp::HasWork { .. } => true,
763        }
764    }
765
766    /// Is it deferred work
767    pub fn is_deferred_work(&self) -> bool {
768        match self {
769            WorkResp::NoWork => false,
770            WorkResp::HasWork { work_description } => work_description.is_deferred_work(),
771        }
772    }
773
774    /// Is it deferred work
775    pub fn is_rebalance(&self) -> bool {
776        match self {
777            WorkResp::NoWork => false,
778            WorkResp::HasWork { work_description } => work_description.is_rebalance(),
779        }
780    }
781
782    /// Is it compute lp token work ?
783    pub fn is_compute_lp_token(&self) -> bool {
784        match self {
785            WorkResp::NoWork => false,
786            WorkResp::HasWork { work_description } => work_description.is_compute_lp_token(),
787        }
788    }
789
790    /// Is it reset status work ?
791    pub fn is_reset_status(&self) -> bool {
792        match self {
793            WorkResp::NoWork => false,
794            WorkResp::HasWork { work_description } => work_description.is_reset_status(),
795        }
796    }
797}
798
799/// Work Description
800#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
801#[serde(rename_all = "snake_case")]
802pub enum WorkDescription {
803    /// Load Market
804    LoadMarket {},
805    /// Calculate LP token value
806    ComputeLpTokenValue {
807        /// Token
808        token: Token,
809        /// Start from specific market id in the processing phase
810        process_start_from: Option<MarketId>,
811        /// Start from specific market id in the validate phase
812        validate_start_from: Option<MarketId>,
813    },
814    /// Process Queue item
815    ProcessQueueItem {
816        /// Id to process
817        id: QueuePositionId,
818    },
819    /// Reset market specific statistics
820    ResetStats {
821        /// Token
822        token: Token,
823    },
824    /// Handle deferred exec id
825    HandleDeferredExecId {},
826    /// Rebalance is done when contract balance is not same as the one
827    /// internally tracked by it.
828    Rebalance {
829        /// Token
830        token: Token,
831        /// Amount that needs to be balanced
832        amount: NonZero<Collateral>,
833        /// Start from specific market id
834        start_from: Option<MarketId>,
835    },
836}
837
838impl WorkDescription {
839    /// Is it the rebalance work ?
840    pub fn is_rebalance(&self) -> bool {
841        match self {
842            WorkDescription::LoadMarket {} => false,
843            WorkDescription::ComputeLpTokenValue { .. } => false,
844            WorkDescription::ProcessQueueItem { .. } => false,
845            WorkDescription::ResetStats { .. } => false,
846            WorkDescription::HandleDeferredExecId {} => false,
847            WorkDescription::Rebalance { .. } => true,
848        }
849    }
850
851    /// Is it compute lp token work ?
852    pub fn is_compute_lp_token(&self) -> bool {
853        match self {
854            WorkDescription::LoadMarket {} => false,
855            WorkDescription::ComputeLpTokenValue { .. } => true,
856            WorkDescription::ProcessQueueItem { .. } => false,
857            WorkDescription::ResetStats { .. } => false,
858            WorkDescription::HandleDeferredExecId {} => false,
859            WorkDescription::Rebalance { .. } => false,
860        }
861    }
862
863    /// Is it reset stats ?
864    pub fn is_reset_status(&self) -> bool {
865        match self {
866            WorkDescription::LoadMarket {} => false,
867            WorkDescription::ComputeLpTokenValue { .. } => false,
868            WorkDescription::ProcessQueueItem { .. } => false,
869            WorkDescription::ResetStats { .. } => true,
870            WorkDescription::HandleDeferredExecId {} => false,
871            WorkDescription::Rebalance { .. } => false,
872        }
873    }
874
875    /// Is deferred work ?
876    pub fn is_deferred_work(&self) -> bool {
877        match self {
878            WorkDescription::LoadMarket {} => false,
879            WorkDescription::ComputeLpTokenValue { .. } => false,
880            WorkDescription::ProcessQueueItem { .. } => false,
881            WorkDescription::ResetStats { .. } => false,
882            WorkDescription::HandleDeferredExecId {} => true,
883            WorkDescription::Rebalance { .. } => false,
884        }
885    }
886}
887
888/// Queue position id that needs to be processed
889#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
890#[serde(rename_all = "snake_case")]
891pub enum QueuePositionId {
892    /// Queue position id corrsponding to the queue items that will
893    /// increase or won't change the collateral
894    IncQueuePositionId(IncQueuePositionId),
895    /// Queue position id corresponding to the queue items that will
896    /// decrease the collateral
897    DecQueuePositionId(DecQueuePositionId),
898}
899
900impl<'a> PrimaryKey<'a> for QueuePositionId {
901    type Prefix = ();
902    type SubPrefix = ();
903    type Suffix = Self;
904    type SuperSuffix = Self;
905
906    fn key(&self) -> Vec<Key> {
907        let (queue_type, key) = match self {
908            QueuePositionId::IncQueuePositionId(id) => (0u8, id.key()),
909            QueuePositionId::DecQueuePositionId(id) => (1u8, id.key()),
910        };
911        let mut keys = vec![Key::Val8([queue_type])];
912        keys.extend(key);
913        keys
914    }
915}
916
917impl KeyDeserialize for QueuePositionId {
918    type Output = QueuePositionId;
919
920    const KEY_ELEMS: u16 = 2;
921
922    fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
923        let (queue_type, queue_id) = <(u8, u64) as KeyDeserialize>::from_vec(value)?;
924        let position_id = match queue_type {
925            0 => QueuePositionId::IncQueuePositionId(IncQueuePositionId(queue_id.into())),
926            1 => QueuePositionId::DecQueuePositionId(DecQueuePositionId(queue_id.into())),
927            _ => {
928                return Err(StdError::serialize_err(
929                    "QueuePositionId",
930                    "Invalid number in queue_type",
931                ))
932            }
933        };
934        Ok(position_id)
935    }
936}
937
938impl<'a> Prefixer<'a> for QueuePositionId {
939    fn prefix(&self) -> Vec<Key> {
940        match self {
941            QueuePositionId::IncQueuePositionId(id) => {
942                let mut keys = vec![Key::Val8([0u8])];
943                keys.extend(id.key());
944                keys
945            }
946            QueuePositionId::DecQueuePositionId(id) => {
947                let mut keys = vec![Key::Val8([1u8])];
948                keys.extend(id.key());
949                keys
950            }
951        }
952    }
953}
954
955/// Queue position number
956#[derive(
957    Copy, PartialOrd, Ord, Eq, Clone, PartialEq, serde::Serialize, serde::Deserialize, Debug,
958)]
959#[serde(rename_all = "snake_case")]
960pub struct IncQueuePositionId(Uint64);
961
962impl IncQueuePositionId {
963    /// Construct a new value from a [u64].
964    pub fn new(x: u64) -> Self {
965        IncQueuePositionId(x.into())
966    }
967
968    /// The underlying `u64` representation.
969    pub fn u64(self) -> u64 {
970        self.0.u64()
971    }
972
973    /// Generate the next position ID
974    ///
975    /// Panics on overflow
976    pub fn next(self) -> Self {
977        IncQueuePositionId((self.u64() + 1).into())
978    }
979}
980
981impl<'a> PrimaryKey<'a> for IncQueuePositionId {
982    type Prefix = ();
983    type SubPrefix = ();
984    type Suffix = Self;
985    type SuperSuffix = Self;
986
987    fn key(&self) -> Vec<Key> {
988        vec![Key::Val64(self.0.u64().to_cw_bytes())]
989    }
990}
991
992impl<'a> Prefixer<'a> for IncQueuePositionId {
993    fn prefix(&self) -> Vec<Key> {
994        vec![Key::Val64(self.0.u64().to_cw_bytes())]
995    }
996}
997
998impl KeyDeserialize for IncQueuePositionId {
999    type Output = IncQueuePositionId;
1000
1001    const KEY_ELEMS: u16 = 1;
1002
1003    #[inline(always)]
1004    fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
1005        u64::from_vec(value).map(|x| IncQueuePositionId(Uint64::new(x)))
1006    }
1007}
1008
1009impl std::fmt::Display for IncQueuePositionId {
1010    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1011        write!(f, "{}", self.0)
1012    }
1013}
1014
1015impl FromStr for IncQueuePositionId {
1016    type Err = ParseIntError;
1017    fn from_str(src: &str) -> Result<Self, ParseIntError> {
1018        src.parse().map(|x| IncQueuePositionId(Uint64::new(x)))
1019    }
1020}
1021
1022/// Queue position number
1023#[derive(
1024    Copy, PartialOrd, Ord, Eq, Clone, PartialEq, serde::Serialize, serde::Deserialize, Debug,
1025)]
1026#[serde(rename_all = "snake_case")]
1027pub struct DecQueuePositionId(Uint64);
1028
1029impl DecQueuePositionId {
1030    /// Construct a new value from a [u64].
1031    pub fn new(x: u64) -> Self {
1032        DecQueuePositionId(x.into())
1033    }
1034
1035    /// The underlying `u64` representation.
1036    pub fn u64(self) -> u64 {
1037        self.0.u64()
1038    }
1039
1040    /// Generate the next position ID
1041    ///
1042    /// Panics on overflow
1043    pub fn next(self) -> Self {
1044        DecQueuePositionId((self.u64() + 1).into())
1045    }
1046}
1047
1048impl<'a> PrimaryKey<'a> for DecQueuePositionId {
1049    type Prefix = ();
1050    type SubPrefix = ();
1051    type Suffix = Self;
1052    type SuperSuffix = Self;
1053
1054    fn key(&self) -> Vec<Key> {
1055        vec![Key::Val64(self.0.u64().to_cw_bytes())]
1056    }
1057}
1058
1059impl<'a> Prefixer<'a> for DecQueuePositionId {
1060    fn prefix(&self) -> Vec<Key> {
1061        vec![Key::Val64(self.0.u64().to_cw_bytes())]
1062    }
1063}
1064
1065impl KeyDeserialize for DecQueuePositionId {
1066    type Output = DecQueuePositionId;
1067
1068    const KEY_ELEMS: u16 = 1;
1069
1070    #[inline(always)]
1071    fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
1072        u64::from_vec(value).map(|x| DecQueuePositionId(Uint64::new(x)))
1073    }
1074}
1075
1076impl std::fmt::Display for DecQueuePositionId {
1077    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1078        write!(f, "{}", self.0)
1079    }
1080}
1081
1082impl FromStr for DecQueuePositionId {
1083    type Err = ParseIntError;
1084    fn from_str(src: &str) -> Result<Self, ParseIntError> {
1085        src.parse().map(|x| DecQueuePositionId(Uint64::new(x)))
1086    }
1087}
1088
1089/// Migration message, currently no fields needed
1090#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
1091#[serde(rename_all = "snake_case")]
1092pub struct MigrateMsg {}