injective_cosmwasm/
msg.rs

1use cosmwasm_std::{Addr, BankMsg, Coin, CosmosMsg, CustomMsg, Deps, StdError, StdResult};
2use injective_math::FPDecimal;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::exchange::{
7    derivative::{derivative_order_to_short, ShortDerivativeOrder},
8    order::{order_data_to_short, OrderData, ShortOrderData},
9    privileged_action::coins_to_string,
10    spot::{spot_order_to_short, ShortSpotOrder, SpotOrder},
11    subaccount::{is_default_subaccount, subaccount_id_to_injective_address},
12    types::{MarketId, SubaccountId},
13};
14use crate::InjectiveQueryWrapper;
15use crate::{exchange::derivative::DerivativeOrder, oracle::types::PriceAttestation, route::InjectiveRoute};
16
17#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
18#[serde(rename_all = "snake_case")]
19pub struct InjectiveMsgWrapper {
20    pub route: InjectiveRoute,
21    pub msg_data: InjectiveMsg,
22}
23
24impl From<InjectiveMsgWrapper> for CosmosMsg<InjectiveMsgWrapper> {
25    fn from(s: InjectiveMsgWrapper) -> CosmosMsg<InjectiveMsgWrapper> {
26        CosmosMsg::Custom(s)
27    }
28}
29
30impl CustomMsg for InjectiveMsgWrapper {}
31
32/// InjectiveMsg is an override of CosmosMsg::Custom to add support for Injective's custom message types
33#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
34#[serde(rename_all = "snake_case")]
35pub enum InjectiveMsg {
36    Deposit {
37        sender: Addr,
38        subaccount_id: SubaccountId,
39        amount: Coin,
40    },
41    Withdraw {
42        sender: Addr,
43        subaccount_id: SubaccountId,
44        amount: Coin,
45    },
46    SubaccountTransfer {
47        sender: Addr,
48        source_subaccount_id: SubaccountId,
49        destination_subaccount_id: SubaccountId,
50        amount: Coin,
51    },
52    ExternalTransfer {
53        sender: Addr,
54        source_subaccount_id: SubaccountId,
55        destination_subaccount_id: SubaccountId,
56        amount: Coin,
57    },
58    CreateSpotMarketOrder {
59        sender: Addr,
60        order: SpotOrder,
61    },
62    CreateDerivativeMarketOrder {
63        sender: Addr,
64        order: DerivativeOrder,
65    },
66    CancelDerivativeOrder {
67        sender: Addr,
68        market_id: MarketId,
69        subaccount_id: SubaccountId,
70        order_hash: String,
71        order_mask: i32,
72    },
73    CancelSpotOrder {
74        sender: Addr,
75        market_id: MarketId,
76        subaccount_id: SubaccountId,
77        order_hash: String,
78    },
79    IncreasePositionMargin {
80        sender: Addr,
81        source_subaccount_id: SubaccountId,
82        destination_subaccount_id: SubaccountId,
83        market_id: MarketId,
84        amount: FPDecimal,
85    },
86    PrivilegedExecuteContract {
87        sender: Addr,
88        funds: String, // TODO consider adding custom Vec<Coin> type with custom serializer using coins_to_string
89        contract_address: Addr,
90        data: String,
91    },
92    LiquidatePosition {
93        sender: Addr,
94        subaccount_id: SubaccountId,
95        market_id: MarketId,
96        order: Option<DerivativeOrder>,
97    },
98    RewardsOptOut {
99        sender: Addr,
100    },
101    BatchUpdateOrders {
102        sender: Addr,
103        subaccount_id: Option<SubaccountId>,
104        spot_market_ids_to_cancel_all: Vec<MarketId>,
105        derivative_market_ids_to_cancel_all: Vec<MarketId>,
106        spot_orders_to_cancel: Vec<ShortOrderData>,
107        derivative_orders_to_cancel: Vec<ShortOrderData>,
108        spot_orders_to_create: Vec<ShortSpotOrder>,
109        derivative_orders_to_create: Vec<ShortDerivativeOrder>,
110    },
111    RelayPythPrices {
112        sender: Addr,
113        price_attestations: Vec<PriceAttestation>,
114    },
115    CreateDenom {
116        sender: String,
117        subdenom: String,
118    },
119    /// Contracts can mint native tokens for an existing factory denom
120    /// that they are the admin of.
121    Mint {
122        sender: Addr,
123        amount: Coin,
124        mint_to: String,
125    },
126    /// Contracts can burn native tokens for an existing factory denom
127    /// that they are the admin of.
128    /// Currently, the burn from address must be the admin contract.
129    Burn {
130        sender: Addr,
131        amount: Coin,
132    },
133    /// Sets metadata of token-factory token
134    SetTokenMetadata {
135        denom: String,
136        name: String,
137        symbol: String,
138        decimals: u8,
139    },
140    /// Wasmx - update contract
141    UpdateContract {
142        sender: Addr,
143        contract_address: Addr,
144        gas_limit: u64,
145        gas_price: u64,
146        admin_address: String,
147    },
148    ActivateContract {
149        sender: Addr,
150        contract_address: Addr,
151    },
152    DeactivateContract {
153        sender: Addr,
154        contract_address: Addr,
155    },
156}
157
158pub fn create_deposit_msg(sender: Addr, subaccount_id: SubaccountId, amount: Coin) -> CosmosMsg<InjectiveMsgWrapper> {
159    InjectiveMsgWrapper {
160        route: InjectiveRoute::Exchange,
161        msg_data: InjectiveMsg::Deposit {
162            sender,
163            subaccount_id,
164            amount,
165        },
166    }
167    .into()
168}
169
170pub fn create_withdraw_msg(sender: Addr, subaccount_id: SubaccountId, amount: Coin) -> CosmosMsg<InjectiveMsgWrapper> {
171    InjectiveMsgWrapper {
172        route: InjectiveRoute::Exchange,
173        msg_data: InjectiveMsg::Withdraw {
174            sender,
175            subaccount_id,
176            amount,
177        },
178    }
179    .into()
180}
181
182pub fn create_subaccount_transfer_msg(
183    deps: &Deps<InjectiveQueryWrapper>,
184    source_subaccount_id: &SubaccountId,
185    destination_subaccount_id: &SubaccountId,
186    amount: &Coin,
187) -> StdResult<Vec<CosmosMsg<InjectiveMsgWrapper>>> {
188    let sender = subaccount_id_to_injective_address(source_subaccount_id, deps)?;
189    let to_address = subaccount_id_to_injective_address(destination_subaccount_id, deps)?;
190
191    let is_external_transfer = sender != to_address;
192    if is_external_transfer {
193        return create_external_transfer_msg(deps, source_subaccount_id, destination_subaccount_id, amount);
194    }
195
196    if is_default_subaccount(destination_subaccount_id) {
197        return Ok(vec![create_withdraw_msg(sender, source_subaccount_id.to_owned(), amount.to_owned())]);
198    }
199
200    Ok(vec![InjectiveMsgWrapper {
201        route: InjectiveRoute::Exchange,
202        msg_data: InjectiveMsg::SubaccountTransfer {
203            sender,
204            source_subaccount_id: source_subaccount_id.to_owned(),
205            destination_subaccount_id: destination_subaccount_id.to_owned(),
206            amount: amount.to_owned(),
207        },
208    }
209    .into()])
210}
211
212pub fn create_external_transfer_msg(
213    deps: &Deps<InjectiveQueryWrapper>,
214    source_subaccount_id: &SubaccountId,
215    destination_subaccount_id: &SubaccountId,
216    amount: &Coin,
217) -> StdResult<Vec<CosmosMsg<InjectiveMsgWrapper>>> {
218    let sender = subaccount_id_to_injective_address(source_subaccount_id, deps)?;
219
220    if is_default_subaccount(destination_subaccount_id) {
221        let to_address = subaccount_id_to_injective_address(destination_subaccount_id, deps)?;
222
223        let bank_send_msg = CosmosMsg::Bank(BankMsg::Send {
224            to_address: to_address.to_string(),
225            amount: vec![amount.to_owned()],
226        });
227
228        if is_default_subaccount(source_subaccount_id) {
229            return Ok(vec![bank_send_msg]);
230        }
231
232        let withdraw_msg = create_withdraw_msg(sender, source_subaccount_id.to_owned(), amount.to_owned());
233        return Ok(vec![withdraw_msg, bank_send_msg]);
234    }
235
236    if is_default_subaccount(source_subaccount_id) {
237        return Err(StdError::generic_err("Cannot send from default subaccount to external subaccount"));
238    }
239
240    Ok(vec![InjectiveMsgWrapper {
241        route: InjectiveRoute::Exchange,
242        msg_data: InjectiveMsg::ExternalTransfer {
243            sender,
244            source_subaccount_id: source_subaccount_id.to_owned(),
245            destination_subaccount_id: destination_subaccount_id.to_owned(),
246            amount: amount.to_owned(),
247        },
248    }
249    .into()])
250}
251
252pub fn create_spot_market_order_msg(sender: Addr, order: SpotOrder) -> CosmosMsg<InjectiveMsgWrapper> {
253    InjectiveMsgWrapper {
254        route: InjectiveRoute::Exchange,
255        msg_data: InjectiveMsg::CreateSpotMarketOrder { sender, order },
256    }
257    .into()
258}
259
260pub fn create_derivative_market_order_msg(sender: Addr, order: DerivativeOrder) -> CosmosMsg<InjectiveMsgWrapper> {
261    InjectiveMsgWrapper {
262        route: InjectiveRoute::Exchange,
263        msg_data: InjectiveMsg::CreateDerivativeMarketOrder { sender, order },
264    }
265    .into()
266}
267
268pub fn cancel_spot_order_msg(sender: Addr, market_id: MarketId, subaccount_id: SubaccountId, order_hash: String) -> CosmosMsg<InjectiveMsgWrapper> {
269    InjectiveMsgWrapper {
270        route: InjectiveRoute::Exchange,
271        msg_data: InjectiveMsg::CancelSpotOrder {
272            sender,
273            market_id,
274            subaccount_id,
275            order_hash,
276        },
277    }
278    .into()
279}
280
281pub fn cancel_derivative_order_msg(
282    sender: Addr,
283    market_id: MarketId,
284    subaccount_id: SubaccountId,
285    order_hash: String,
286    order_mask: i32,
287) -> CosmosMsg<InjectiveMsgWrapper> {
288    InjectiveMsgWrapper {
289        route: InjectiveRoute::Exchange,
290        msg_data: InjectiveMsg::CancelDerivativeOrder {
291            sender,
292            market_id,
293            subaccount_id,
294            order_hash,
295            order_mask,
296        },
297    }
298    .into()
299}
300
301pub fn create_increase_position_margin_msg(
302    sender: Addr,
303    source_subaccount_id: SubaccountId,
304    destination_subaccount_id: SubaccountId,
305    market_id: MarketId,
306    amount: FPDecimal,
307) -> CosmosMsg<InjectiveMsgWrapper> {
308    InjectiveMsgWrapper {
309        route: InjectiveRoute::Exchange,
310        msg_data: InjectiveMsg::IncreasePositionMargin {
311            sender,
312            source_subaccount_id,
313            destination_subaccount_id,
314            market_id,
315            amount,
316        },
317    }
318    .into()
319}
320
321pub fn create_privileged_execute_contract_msg(
322    sender: Addr,
323    funds: Vec<Coin>,
324    contract_address: Addr,
325    data: String,
326) -> CosmosMsg<InjectiveMsgWrapper> {
327    InjectiveMsgWrapper {
328        route: InjectiveRoute::Exchange,
329        msg_data: InjectiveMsg::PrivilegedExecuteContract {
330            sender,
331            funds: coins_to_string(funds),
332            contract_address,
333            data,
334        },
335    }
336    .into()
337}
338
339pub fn create_liquidate_position_msg(
340    sender: Addr,
341    subaccount_id: SubaccountId,
342    market_id: MarketId,
343    order: Option<DerivativeOrder>,
344) -> CosmosMsg<InjectiveMsgWrapper> {
345    InjectiveMsgWrapper {
346        route: InjectiveRoute::Exchange,
347        msg_data: InjectiveMsg::LiquidatePosition {
348            sender,
349            subaccount_id,
350            market_id,
351            order,
352        },
353    }
354    .into()
355}
356
357pub fn create_rewards_opt_out_msg(sender: Addr) -> CosmosMsg<InjectiveMsgWrapper> {
358    InjectiveMsgWrapper {
359        route: InjectiveRoute::Exchange,
360        msg_data: InjectiveMsg::RewardsOptOut { sender },
361    }
362    .into()
363}
364
365pub fn create_batch_update_orders_msg(
366    sender: Addr,
367    subaccount_id: Option<SubaccountId>,
368    spot_market_ids_to_cancel_all: Vec<MarketId>,
369    derivative_market_ids_to_cancel_all: Vec<MarketId>,
370    spot_orders_to_cancel: Vec<OrderData>,
371    derivative_orders_to_cancel: Vec<OrderData>,
372    spot_orders_to_create: Vec<SpotOrder>,
373    derivative_orders_to_create: Vec<DerivativeOrder>,
374) -> CosmosMsg<InjectiveMsgWrapper> {
375    InjectiveMsgWrapper {
376        route: InjectiveRoute::Exchange,
377        msg_data: InjectiveMsg::BatchUpdateOrders {
378            sender,
379            subaccount_id,
380            spot_market_ids_to_cancel_all,
381            derivative_market_ids_to_cancel_all,
382            spot_orders_to_cancel: order_data_to_short(spot_orders_to_cancel),
383            derivative_orders_to_cancel: order_data_to_short(derivative_orders_to_cancel),
384            spot_orders_to_create: spot_order_to_short(spot_orders_to_create),
385            derivative_orders_to_create: derivative_order_to_short(derivative_orders_to_create),
386        },
387    }
388    .into()
389}
390
391pub fn create_relay_pyth_prices_msg(sender: Addr, price_attestations: Vec<PriceAttestation>) -> CosmosMsg<InjectiveMsgWrapper> {
392    InjectiveMsgWrapper {
393        route: InjectiveRoute::Oracle,
394        msg_data: InjectiveMsg::RelayPythPrices { sender, price_attestations },
395    }
396    .into()
397}
398
399pub fn create_mint_tokens_msg(sender: Addr, amount: Coin, mint_to: String) -> CosmosMsg<InjectiveMsgWrapper> {
400    InjectiveMsgWrapper {
401        route: InjectiveRoute::Tokenfactory,
402        msg_data: InjectiveMsg::Mint { sender, amount, mint_to },
403    }
404    .into()
405}
406
407pub fn create_burn_tokens_msg(sender: Addr, amount: Coin) -> CosmosMsg<InjectiveMsgWrapper> {
408    InjectiveMsgWrapper {
409        route: InjectiveRoute::Tokenfactory,
410        msg_data: InjectiveMsg::Burn { sender, amount },
411    }
412    .into()
413}
414
415pub fn create_new_denom_msg(sender: String, subdenom: String) -> CosmosMsg<InjectiveMsgWrapper> {
416    InjectiveMsgWrapper {
417        route: InjectiveRoute::Tokenfactory,
418        msg_data: InjectiveMsg::CreateDenom { sender, subdenom },
419    }
420    .into()
421}
422
423pub fn create_set_token_metadata_msg(denom: String, name: String, symbol: String, decimals: u8) -> CosmosMsg<InjectiveMsgWrapper> {
424    InjectiveMsgWrapper {
425        route: InjectiveRoute::Tokenfactory,
426        msg_data: InjectiveMsg::SetTokenMetadata {
427            denom,
428            name,
429            symbol,
430            decimals,
431        },
432    }
433    .into()
434}
435
436pub fn create_update_contract_msg(
437    sender: Addr,
438    contract_address: Addr,
439    gas_limit: u64,
440    gas_price: u64,
441    admin_address: Option<Addr>,
442) -> CosmosMsg<InjectiveMsgWrapper> {
443    let admin = match admin_address {
444        None => "".to_string(),
445        Some(addr) => addr.to_string(),
446    };
447    InjectiveMsgWrapper {
448        route: InjectiveRoute::Wasmx,
449        msg_data: InjectiveMsg::UpdateContract {
450            sender,
451            contract_address,
452            gas_limit,
453            gas_price,
454            admin_address: admin,
455        },
456    }
457    .into()
458}
459
460pub fn create_activate_contract_msg(sender: Addr, contract_address: Addr) -> CosmosMsg<InjectiveMsgWrapper> {
461    InjectiveMsgWrapper {
462        route: InjectiveRoute::Wasmx,
463        msg_data: InjectiveMsg::ActivateContract { sender, contract_address },
464    }
465    .into()
466}
467
468pub fn create_deactivate_contract_msg(sender: Addr, contract_address: Addr) -> CosmosMsg<InjectiveMsgWrapper> {
469    InjectiveMsgWrapper {
470        route: InjectiveRoute::Wasmx,
471        msg_data: InjectiveMsg::DeactivateContract { sender, contract_address },
472    }
473    .into()
474}