1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::types::{Step, Swap, SwapAmount};
use cosmwasm_std::{Coin, CustomQuery, Decimal, Uint128};

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum OsmosisQuery {
    /// Given a sub-denom minted by a contract via `OsmosisMsg::MintTokens`,
    /// returns the full denom as used by `BankMsg::Send`.
    /// You may call `FullDenom { contract: env.contract.address, sub_denom }` to find the denom issued
    /// by the current contract.
    FullDenom { contract: String, sub_denom: String },
    /// For a given pool ID, list all tokens traded on it with current liquidity (spot).
    /// As well as the total number of LP shares and their denom
    PoolState { id: u64 },
    /// Return current spot price swapping In for Out on given pool ID.
    /// Warning: this can easily be manipulated via sandwich attacks, do not use as price oracle.
    /// We will add TWAP for more robust price feed.
    SpotPrice { swap: Swap, with_swap_fee: bool },
    /// Return current spot price swapping In for Out on given pool ID.
    /// You can call `EstimatePrice { contract: env.contract.address, ... }` to set sender to the
    /// current contract.
    /// Warning: this can easily be manipulated via sandwich attacks, do not use as price oracle.
    /// We will add TWAP for more robust price feed.
    EstimatePrice {
        contract: String,
        first: Swap,
        route: Vec<Step>,
        amount: SwapAmount,
    },
}

impl CustomQuery for OsmosisQuery {}

impl OsmosisQuery {
    /// Calculate spot price without swap fee
    pub fn spot_price(pool_id: u64, denom_in: &str, denom_out: &str) -> Self {
        OsmosisQuery::SpotPrice {
            swap: Swap::new(pool_id, denom_in, denom_out),
            with_swap_fee: false,
        }
    }

    /// Basic helper to estimate price of a swap on one pool
    pub fn estimate_price(
        contract: impl Into<String>,
        pool_id: u64,
        denom_in: impl Into<String>,
        denom_out: impl Into<String>,
        amount: SwapAmount,
    ) -> Self {
        OsmosisQuery::EstimatePrice {
            contract: contract.into(),
            first: Swap::new(pool_id, denom_in, denom_out),
            amount,
            route: vec![],
        }
    }
}

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct FullDenomResponse {
    pub denom: String,
}

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct PoolStateResponse {
    /// The various assets that be swapped. Including current liquidity.
    pub assets: Vec<Coin>,
    /// The number of lp shares and their amount
    pub shares: Coin,
}

impl PoolStateResponse {
    pub fn has_denom(&self, denom: &str) -> bool {
        self.assets.iter().any(|c| c.denom == denom)
    }

    pub fn lp_denom(&self) -> &str {
        &self.shares.denom
    }

    /// If I hold num_shares of the lp_denom, how many assets does that equate to?
    pub fn shares_value(&self, num_shares: impl Into<Uint128>) -> Vec<Coin> {
        let num_shares = num_shares.into();
        self.assets
            .iter()
            .map(|c| Coin {
                denom: c.denom.clone(),
                amount: c.amount * num_shares / self.shares.amount,
            })
            .collect()
    }
}

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct SpotPriceResponse {
    /// How many output we would get for 1 input
    pub price: Decimal,
}

#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
pub struct EstimatePriceResponse {
    // If you query with SwapAmount::Input, this is SwapAmount::Output
    // If you query with SwapAmount::Output, this is SwapAmount::Input
    pub amount: SwapAmount,
}