use std::{fmt, num::ParseIntError};
use crate::constants::event_key;
use crate::prelude::*;
use cosmwasm_std::StdResult;
use cw_storage_plus::{IntKey, Key, KeyDeserialize, Prefixer, PrimaryKey};
use super::{
entry::{SlippageAssert, StopLoss},
order::OrderId,
position::PositionId,
};
#[cw_serde]
#[derive(Copy, PartialOrd, Ord, Eq)]
pub struct DeferredExecId(Uint64);
impl std::hash::Hash for DeferredExecId {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.u64().hash(state);
}
}
impl DeferredExecId {
pub fn first() -> Self {
DeferredExecId(Uint64::one())
}
pub fn next(self) -> Self {
DeferredExecId((self.0.u64() + 1).into())
}
pub fn u64(self) -> u64 {
self.0.u64()
}
pub fn from_u64(x: u64) -> Self {
DeferredExecId(x.into())
}
}
impl<'a> PrimaryKey<'a> for DeferredExecId {
type Prefix = ();
type SubPrefix = ();
type Suffix = Self;
type SuperSuffix = Self;
fn key(&self) -> Vec<Key> {
vec![Key::Val64(self.0.u64().to_cw_bytes())]
}
}
impl<'a> Prefixer<'a> for DeferredExecId {
fn prefix(&self) -> Vec<Key> {
vec![Key::Val64(self.0.u64().to_cw_bytes())]
}
}
impl KeyDeserialize for DeferredExecId {
type Output = DeferredExecId;
const KEY_ELEMS: u16 = 1;
#[inline(always)]
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
u64::from_vec(value).map(|x| DeferredExecId(Uint64::new(x)))
}
}
impl fmt::Display for DeferredExecId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromStr for DeferredExecId {
type Err = ParseIntError;
fn from_str(src: &str) -> Result<Self, ParseIntError> {
src.parse().map(|x| DeferredExecId(Uint64::new(x)))
}
}
#[cw_serde]
pub struct ListDeferredExecsResp {
pub items: Vec<DeferredExecWithStatus>,
pub next_start_after: Option<DeferredExecId>,
}
#[cw_serde]
pub enum GetDeferredExecResp {
Found {
item: Box<DeferredExecWithStatus>,
},
NotFound {},
}
#[cw_serde]
pub struct DeferredExecWithStatus {
pub id: DeferredExecId,
pub created: Timestamp,
pub status: DeferredExecStatus,
pub owner: Addr,
pub item: DeferredExecItem,
}
#[cw_serde]
pub enum DeferredExecStatus {
Pending,
Success {
target: DeferredExecCompleteTarget,
executed: Timestamp,
},
Failure {
reason: String,
executed: Timestamp,
crank_price: Option<PricePoint>,
},
}
impl DeferredExecStatus {
pub fn is_pending(&self) -> bool {
match self {
DeferredExecStatus::Pending => true,
DeferredExecStatus::Success { .. } => false,
DeferredExecStatus::Failure { .. } => false,
}
}
pub fn is_failure(&self) -> bool {
match self {
DeferredExecStatus::Pending => false,
DeferredExecStatus::Success { .. } => false,
DeferredExecStatus::Failure { .. } => true,
}
}
pub fn is_success(&self) -> bool {
match self {
DeferredExecStatus::Pending => false,
DeferredExecStatus::Success { .. } => true,
DeferredExecStatus::Failure { .. } => false,
}
}
}
#[cw_serde]
#[allow(clippy::large_enum_variant)]
pub enum DeferredExecItem {
OpenPosition {
slippage_assert: Option<SlippageAssert>,
leverage: LeverageToBase,
direction: DirectionToBase,
#[deprecated(note = "use take_profit instead")]
max_gains: Option<MaxGainsInQuote>,
stop_loss_override: Option<PriceBaseInQuote>,
#[serde(alias = "take_profit_override")]
take_profit: Option<TakeProfitTrader>,
amount: NonZero<Collateral>,
crank_fee: Collateral,
crank_fee_usd: Usd,
},
UpdatePositionAddCollateralImpactLeverage {
id: PositionId,
amount: NonZero<Collateral>,
},
UpdatePositionAddCollateralImpactSize {
id: PositionId,
slippage_assert: Option<SlippageAssert>,
amount: NonZero<Collateral>,
},
UpdatePositionRemoveCollateralImpactLeverage {
id: PositionId,
amount: NonZero<Collateral>,
},
UpdatePositionRemoveCollateralImpactSize {
id: PositionId,
amount: NonZero<Collateral>,
slippage_assert: Option<SlippageAssert>,
},
UpdatePositionLeverage {
id: PositionId,
leverage: LeverageToBase,
slippage_assert: Option<SlippageAssert>,
},
UpdatePositionMaxGains {
id: PositionId,
max_gains: MaxGainsInQuote,
},
UpdatePositionTakeProfitPrice {
id: PositionId,
price: TakeProfitTrader,
},
UpdatePositionStopLossPrice {
id: PositionId,
stop_loss: StopLoss,
},
ClosePosition {
id: PositionId,
slippage_assert: Option<SlippageAssert>,
},
SetTriggerOrder {
id: PositionId,
stop_loss_override: Option<PriceBaseInQuote>,
#[serde(alias = "take_profit_override")]
take_profit: Option<TakeProfitTrader>,
},
PlaceLimitOrder {
trigger_price: PriceBaseInQuote,
leverage: LeverageToBase,
direction: DirectionToBase,
#[deprecated(note = "use take_profit instead")]
max_gains: Option<MaxGainsInQuote>,
stop_loss_override: Option<PriceBaseInQuote>,
#[serde(alias = "take_profit_override")]
take_profit: Option<TakeProfitTrader>,
amount: NonZero<Collateral>,
crank_fee: Collateral,
crank_fee_usd: Usd,
},
CancelLimitOrder {
order_id: OrderId,
},
}
#[cw_serde]
#[derive(Copy)]
pub enum DeferredExecTarget {
DoesNotExist,
Position(PositionId),
Order(OrderId),
}
#[cw_serde]
#[derive(Copy)]
pub enum DeferredExecCompleteTarget {
Position(PositionId),
Order(OrderId),
}
impl DeferredExecTarget {
pub fn position_id(&self) -> Option<PositionId> {
match self {
DeferredExecTarget::DoesNotExist | DeferredExecTarget::Order(_) => None,
DeferredExecTarget::Position(pos_id) => Some(*pos_id),
}
}
pub fn order_id(&self) -> Option<OrderId> {
match self {
DeferredExecTarget::DoesNotExist | DeferredExecTarget::Position(_) => None,
DeferredExecTarget::Order(order_id) => Some(*order_id),
}
}
}
impl DeferredExecItem {
pub fn target(&self) -> DeferredExecTarget {
match self {
DeferredExecItem::OpenPosition { .. } => DeferredExecTarget::DoesNotExist,
DeferredExecItem::UpdatePositionAddCollateralImpactLeverage { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::UpdatePositionAddCollateralImpactSize { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::UpdatePositionRemoveCollateralImpactLeverage { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::UpdatePositionRemoveCollateralImpactSize { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::UpdatePositionLeverage { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::UpdatePositionMaxGains { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::UpdatePositionTakeProfitPrice { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::UpdatePositionStopLossPrice { id, .. } => {
DeferredExecTarget::Position(*id)
}
DeferredExecItem::ClosePosition { id, .. } => DeferredExecTarget::Position(*id),
DeferredExecItem::SetTriggerOrder { id, .. } => DeferredExecTarget::Position(*id),
DeferredExecItem::PlaceLimitOrder { .. } => DeferredExecTarget::DoesNotExist,
DeferredExecItem::CancelLimitOrder { order_id } => DeferredExecTarget::Order(*order_id),
}
}
pub fn deposited_amount(&self) -> Collateral {
match self {
DeferredExecItem::OpenPosition { amount, .. }
| DeferredExecItem::UpdatePositionAddCollateralImpactLeverage { amount, .. }
| DeferredExecItem::UpdatePositionAddCollateralImpactSize { amount, .. }
| DeferredExecItem::PlaceLimitOrder { amount, .. } => amount.raw(),
DeferredExecItem::UpdatePositionRemoveCollateralImpactLeverage { .. }
| DeferredExecItem::UpdatePositionRemoveCollateralImpactSize { .. }
| DeferredExecItem::UpdatePositionLeverage { .. }
| DeferredExecItem::UpdatePositionMaxGains { .. }
| DeferredExecItem::UpdatePositionTakeProfitPrice { .. }
| DeferredExecItem::UpdatePositionStopLossPrice { .. }
| DeferredExecItem::ClosePosition { .. }
| DeferredExecItem::SetTriggerOrder { .. }
| DeferredExecItem::CancelLimitOrder { .. } => Collateral::zero(),
}
}
}
#[derive(Clone, Debug)]
pub struct DeferredExecQueuedEvent {
pub deferred_exec_id: DeferredExecId,
pub target: DeferredExecTarget,
pub owner: Addr,
}
impl From<DeferredExecQueuedEvent> for Event {
fn from(
DeferredExecQueuedEvent {
deferred_exec_id,
target,
owner,
}: DeferredExecQueuedEvent,
) -> Self {
let mut event = Event::new("deferred-exec-queued")
.add_attribute(event_key::DEFERRED_EXEC_ID, deferred_exec_id.to_string())
.add_attribute(event_key::DEFERRED_EXEC_OWNER, owner);
match target {
DeferredExecTarget::DoesNotExist => {
event = event.add_attribute(event_key::DEFERRED_EXEC_TARGET, "does-not-exist");
}
DeferredExecTarget::Position(position_id) => {
event = event
.add_attribute(event_key::POS_ID, position_id.to_string())
.add_attribute(event_key::DEFERRED_EXEC_TARGET, "position");
}
DeferredExecTarget::Order(order_id) => {
event = event
.add_attribute(event_key::ORDER_ID, order_id.to_string())
.add_attribute(event_key::DEFERRED_EXEC_TARGET, "order");
}
}
event
}
}
impl TryFrom<Event> for DeferredExecQueuedEvent {
type Error = anyhow::Error;
fn try_from(evt: Event) -> anyhow::Result<Self> {
Ok(Self {
deferred_exec_id: evt
.u64_attr(event_key::DEFERRED_EXEC_ID)
.map(DeferredExecId::from_u64)?,
owner: evt.unchecked_addr_attr(event_key::DEFERRED_EXEC_OWNER)?,
target: match evt.string_attr(event_key::DEFERRED_EXEC_TARGET)?.as_str() {
"does-not-exist" => DeferredExecTarget::DoesNotExist,
"position" => DeferredExecTarget::Position(
evt.u64_attr(event_key::POS_ID).map(PositionId::new)?,
),
"order" => {
DeferredExecTarget::Order(evt.u64_attr(event_key::ORDER_ID).map(OrderId::new)?)
}
_ => anyhow::bail!("invalid deferred exec target"),
},
})
}
}
#[derive(Debug)]
pub struct DeferredExecExecutedEvent {
pub deferred_exec_id: DeferredExecId,
pub target: DeferredExecTarget,
pub owner: Addr,
pub success: bool,
pub desc: String,
}
impl From<DeferredExecExecutedEvent> for Event {
fn from(
DeferredExecExecutedEvent {
deferred_exec_id,
target,
owner,
success,
desc,
}: DeferredExecExecutedEvent,
) -> Self {
let mut event = Event::new("deferred-exec-executed")
.add_attribute(event_key::DEFERRED_EXEC_ID, deferred_exec_id.to_string())
.add_attribute(event_key::DEFERRED_EXEC_OWNER, owner)
.add_attribute(event_key::SUCCESS, if success { "true" } else { "false" })
.add_attribute(event_key::DESC, desc);
match target {
DeferredExecTarget::DoesNotExist => {
event = event.add_attribute(event_key::DEFERRED_EXEC_TARGET, "does-not-exist");
}
DeferredExecTarget::Position(position_id) => {
event = event
.add_attribute(event_key::POS_ID, position_id.to_string())
.add_attribute(event_key::DEFERRED_EXEC_TARGET, "position");
}
DeferredExecTarget::Order(order_id) => {
event = event
.add_attribute(event_key::ORDER_ID, order_id.to_string())
.add_attribute(event_key::DEFERRED_EXEC_TARGET, "order");
}
}
event
}
}
impl TryFrom<Event> for DeferredExecExecutedEvent {
type Error = anyhow::Error;
fn try_from(evt: Event) -> anyhow::Result<Self> {
Ok(Self {
deferred_exec_id: evt
.u64_attr(event_key::DEFERRED_EXEC_ID)
.map(DeferredExecId::from_u64)?,
owner: evt.unchecked_addr_attr(event_key::DEFERRED_EXEC_OWNER)?,
success: evt.bool_attr(event_key::SUCCESS)?,
desc: evt.string_attr(event_key::DESC)?,
target: match evt.string_attr(event_key::DEFERRED_EXEC_TARGET)?.as_str() {
"does-not-exist" => DeferredExecTarget::DoesNotExist,
"position" => DeferredExecTarget::Position(
evt.u64_attr(event_key::POS_ID).map(PositionId::new)?,
),
"order" => {
DeferredExecTarget::Order(evt.u64_attr(event_key::ORDER_ID).map(OrderId::new)?)
}
_ => anyhow::bail!("invalid deferred exec target"),
},
})
}
}
pub struct FeesReturnedEvent {
pub recipient: Addr,
pub amount: NonZero<Collateral>,
pub amount_usd: NonZero<Usd>,
}
impl From<FeesReturnedEvent> for Event {
fn from(
FeesReturnedEvent {
recipient,
amount,
amount_usd,
}: FeesReturnedEvent,
) -> Self {
Event::new("fees-returned")
.add_attribute("recipient", recipient.into_string())
.add_attribute("amount", amount.to_string())
.add_attribute("amount_usd", amount_usd.to_string())
}
}