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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use std::str::FromStr;

use cosmwasm_std::{to_json_binary, Binary, Reply, SubMsgResponse, SubMsgResult};
use enumset::enum_set;
use injective_cosmwasm::{MarketId, OracleType};
use injective_math::FPDecimal;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::vault::{
    AmmInstantiateMsg, AmmOrderType, DerivativeInstantiateMsg, DerivativeRedemptionType, PricingStrategy, SpotInstantiateMsg, SpotRedemptionType,
};

pub const MOCKED_OWNER: &str = "owner";
pub const MOCKED_OWNER_VALID: &str = "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku";
pub const MOCKED_MASTER_ADDRESS: &str = "inj1gfawuv6fslzjlfa4v7exv27mk6rpfeyv823eu2";
pub const MOCKED_MARKET_ID: &str = "0x01edfab47f124748dc89998eb33144af734484ba07099014594321729a0ca16b";
pub const MOCKED_DERIV_MARKET_ID: &str = "0x427aee334987c52fa7b567b2662bdbb68614e48c000000000000000000000000";
pub const MOCKED_USER: &str = "inj1995xnrrtnmtdgjmx0g937vf28dwefhkhy6gy5e";

pub fn mock_cpmm_instantiate_msg(owner: String, master_address: String, market_id: String) -> AmmInstantiateMsg {
    AmmInstantiateMsg {
        config_owner: owner.to_owned(),
        market_id: MarketId::new(market_id).unwrap(),
        master_address,
        order_density: 10u8,
        max_invariant_sensitivity_bps: FPDecimal::from_str("0.1").unwrap(),
        max_price_sensitivity_bps: FPDecimal::from_str("0.1").unwrap(),
        pricing_strategy: PricingStrategy::ConstantPricingWithTickSize(FPDecimal::must_from_str("0.00000000000001")),
        base_decimals: 18u8,
        quote_decimals: 6u8,
        fee_bps: 0u32,
        notional_value_cap: FPDecimal::from(100_000_000_000_000_000_000u128),
        first_subscriber_address: None,
        order_type: AmmOrderType::PostOnly,
    }
}

pub fn mock_scpmm_instantiate_msg(owner: String, master_address: String, market_id: String) -> AmmInstantiateMsg {
    AmmInstantiateMsg {
        config_owner: owner.to_owned(),
        market_id: MarketId::new(market_id).unwrap(),
        master_address,
        order_density: 10u8,
        max_invariant_sensitivity_bps: FPDecimal::from_str("0.1").unwrap(),
        max_price_sensitivity_bps: FPDecimal::from_str("0.1").unwrap(),
        pricing_strategy: PricingStrategy::SmoothingPricingWithRelativePriceRange {
            bid_range: FPDecimal::must_from_str("0.1"),
            ask_range: FPDecimal::must_from_str("0.1"),
        },
        base_decimals: 18u8,
        quote_decimals: 6u8,
        notional_value_cap: FPDecimal::from(100_000_000_000_000_000_000u128),
        fee_bps: 30u32,
        first_subscriber_address: None,
        order_type: AmmOrderType::PostOnly,
    }
}

pub fn mock_spot_instantiate_msg(owner: String, master_address: String, market_id: String) -> SpotInstantiateMsg {
    SpotInstantiateMsg {
        market_id: MarketId::new(market_id).unwrap(),
        order_density: 8,
        reservation_price_sensitivity_ratio: FPDecimal::must_from_str("0.916"),
        reservation_spread_sensitivity_ratio: FPDecimal::ONE,
        max_active_capital_utilization_ratio: FPDecimal::ONE,
        head_change_tolerance_ratio: FPDecimal::ONE,
        head_to_tail_deviation_ratio: FPDecimal::must_from_str("0.05"),
        min_volatility_ratio: FPDecimal::must_from_str("0.002"),
        signed_min_head_to_fair_price_deviation_ratio: FPDecimal::must_from_str("0.01"),
        signed_min_head_to_tob_deviation_ratio: FPDecimal::must_from_str("-0.01"),
        default_mid_price_volatility_ratio: FPDecimal::must_from_str("0.005"),
        target_base_weight: FPDecimal::ONE,
        master_address,
        config_owner: owner.to_owned(),
        oracle_type: OracleType::PriceFeed,
        allowed_redemption_types: enum_set!(SpotRedemptionType::FixedBaseAndQuote),
        base_decimals: 18,
        quote_decimals: 6,
        base_oracle_symbol: "inj".to_string(),
        quote_oracle_symbol: "usdt".to_string(),
        notional_value_cap: FPDecimal::from(999_000_000_000_000_000_000_000_000u128),
        oracle_stale_time: 75,
        min_oracle_volatility_sample_size: 5,
        emergency_oracle_volatility_sample_size: 6,
        oracle_volatility_max_age: 100,
    }
}

pub fn mock_derivative_instantiate_msg(owner: String, master_address: String, market_id: String) -> DerivativeInstantiateMsg {
    DerivativeInstantiateMsg {
        market_id: MarketId::new(market_id).unwrap(),
        leverage: FPDecimal::TWO,
        order_density: 3,
        reservation_price_sensitivity_ratio: FPDecimal::must_from_str("0.1"),
        reservation_spread_sensitivity_ratio: FPDecimal::must_from_str("0.2"),
        max_active_capital_utilization_ratio: FPDecimal::must_from_str("0.8"),
        signed_min_head_to_fair_price_deviation_ratio: FPDecimal::must_from_str("0.1"),
        signed_min_head_to_tob_deviation_ratio: FPDecimal::must_from_str("0.1"),
        head_change_tolerance_ratio: FPDecimal::must_from_str("0.4"),
        min_proximity_to_liquidation: FPDecimal::must_from_str("1.4"),
        min_oracle_volatility_sample_size: 5,
        emergency_oracle_volatility_sample_size: 6,
        oracle_volatility_max_age: 100,
        default_mid_price_volatility_ratio: FPDecimal::must_from_str("0.88"),
        min_volatility_ratio: FPDecimal::must_from_str("0.001"),
        master_address,
        config_owner: owner.to_owned(),
        head_to_tail_deviation_ratio: FPDecimal::must_from_str("0.1"),
        allowed_redemption_types: enum_set!(DerivativeRedemptionType::QuoteOnly | DerivativeRedemptionType::PositionAndQuote),
        position_pnl_penalty: FPDecimal::must_from_str("0.01"),
        notional_value_cap: FPDecimal::from(999_000_000_000_000_000_000u128),
        oracle_stale_time: 60 * 60 * 6,
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct MockedMsgInstantiateContractResponse {
    pub contract_address: String,
    pub data: Option<Binary>,
}

fn encode_bytes(data: Vec<u8>) -> Vec<u8> {
    let mut encoded_data = Vec::new();

    // Field number (1) shifted left by 3 and OR-ed with wire type (2 for length-delimited)
    let field_tag: u8 = (1 << 3) | 2;
    encoded_data.push(field_tag);

    // Length of the data as a varint
    let mut len = data.len();
    while len >= 0x80 {
        encoded_data.push((len as u8) | 0x80);
        len >>= 7;
    }
    encoded_data.push(len as u8);

    // Data itself
    encoded_data.extend(data);

    encoded_data
}

pub fn wrap_as_contract_reply(mut bin_vec: Vec<u8>, reply_id: u64) -> Reply {
    bin_vec = encode_bytes(bin_vec);
    let as_binary = Binary::from(bin_vec);

    Reply {
        id: reply_id,
        result: SubMsgResult::Ok(SubMsgResponse {
            events: vec![],
            data: Some(as_binary),
        }),
    }
}

pub fn mock_instantiate_reply_msg(contract_addr: &str, reply_id: u64) -> Reply {
    let mut bin_vec = to_json_binary(&contract_addr.to_string()).unwrap().as_slice().to_vec();

    // remove encoded double quotes from beginning and end
    bin_vec.remove(0);
    bin_vec.pop();

    wrap_as_contract_reply(bin_vec, reply_id)
}