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#[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, 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 Mint {
122 sender: Addr,
123 amount: Coin,
124 mint_to: String,
125 },
126 Burn {
130 sender: Addr,
131 amount: Coin,
132 },
133 SetTokenMetadata {
135 denom: String,
136 name: String,
137 symbol: String,
138 decimals: u8,
139 },
140 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}