use std::{fmt::Display, num::ParseIntError, str::FromStr};
use super::market::{
entry::{ExecuteMsg as MarketExecuteMsg, SlippageAssert, StopLoss},
order::OrderId,
position::PositionId,
};
use crate::{
number::{Collateral, LpToken, NonZero},
price::{PriceBaseInQuote, PricePoint, TakeProfitTrader},
storage::{DirectionToBase, LeverageToBase, MarketId, RawAddr},
time::Timestamp,
};
use anyhow::{anyhow, bail};
use cosmwasm_std::{Addr, Binary, Decimal256, StdError, StdResult, Uint128, Uint64};
use cw_storage_plus::{IntKey, Key, KeyDeserialize, Prefixer, PrimaryKey};
use thiserror::Error;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct InstantiateMsg {
pub leader: RawAddr,
pub config: ConfigUpdate,
pub parameters: FactoryConfigUpdate,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct Config {
pub factory: Addr,
pub admin: Addr,
pub pending_admin: Option<Addr>,
pub leader: Addr,
pub name: String,
pub description: String,
pub commission_rate: Decimal256,
pub created_at: Timestamp,
pub allowed_rebalance_queries: u32,
pub allowed_lp_token_queries: u32,
}
impl Config {
pub fn check(&self) -> anyhow::Result<()> {
if self.name.len() > 128 {
Err(anyhow!(
"Description should not be more than 128 characters"
))
} else if self.commission_rate < Decimal256::from_ratio(1u32, 100u32) {
Err(anyhow!("Commission rate less than 1 percent"))
} else if self.commission_rate > Decimal256::from_ratio(30u32, 100u32) {
Err(anyhow!("Commission rate greater than 30 percent"))
} else {
Ok(())
}
}
pub fn ensure_leader(&self, sender: &Addr) -> anyhow::Result<()> {
if self.leader != sender {
bail!("Unautorized access, only {} allowed", self.leader)
}
Ok(())
}
pub fn ensure_factory(&self, sender: &Addr) -> anyhow::Result<()> {
if self.factory != sender {
bail!("Unautorized access, only {} allowed", self.factory)
}
Ok(())
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "snake_case")]
#[allow(missing_docs)]
pub struct ConfigUpdate {
pub name: Option<String>,
pub description: Option<String>,
pub commission_rate: Option<Decimal256>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "snake_case")]
#[allow(missing_docs)]
pub struct FactoryConfigUpdate {
pub allowed_rebalance_queries: Option<u32>,
pub allowed_lp_token_queries: Option<u32>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
Receive {
sender: RawAddr,
amount: Uint128,
msg: Binary,
},
Deposit {},
Withdraw {
shares: NonZero<LpToken>,
token: Token,
},
LeaderUpdateConfig(ConfigUpdate),
FactoryUpdateConfig(FactoryConfigUpdate),
LeaderMsg {
market_id: MarketId,
message: Box<MarketExecuteMsg>,
collateral: Option<NonZero<Collateral>>,
},
LeaderWithdrawal {
requested_funds: NonZero<Collateral>,
token: Token,
},
DoWork {},
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
Config {},
QueueStatus {
address: RawAddr,
start_after: Option<QueuePositionId>,
limit: Option<u32>,
},
Balance {
address: RawAddr,
start_after: Option<Token>,
limit: Option<u32>,
},
LeaderStatus {
start_after: Option<Token>,
limit: Option<u32>,
},
HasWork {},
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct QueueResp {
pub items: Vec<QueueItemStatus>,
pub inc_processed_till: Option<IncQueuePositionId>,
pub dec_processed_till: Option<DecQueuePositionId>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub struct QueueItemStatus {
pub item: QueueItem,
pub status: ProcessingStatus,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ProcessingStatus {
NotProcessed,
Finished,
InProgress,
Failed(FailedReason),
}
impl ProcessingStatus {
pub fn failed(&self) -> bool {
match self {
ProcessingStatus::NotProcessed => false,
ProcessingStatus::Finished => false,
ProcessingStatus::Failed(_) => true,
ProcessingStatus::InProgress => false,
}
}
pub fn pending(&self) -> bool {
match self {
ProcessingStatus::NotProcessed => true,
ProcessingStatus::Finished => false,
ProcessingStatus::Failed(_) => false,
ProcessingStatus::InProgress => true,
}
}
pub fn finish(&self) -> bool {
match self {
ProcessingStatus::NotProcessed => false,
ProcessingStatus::Finished => true,
ProcessingStatus::Failed(_) => false,
ProcessingStatus::InProgress => false,
}
}
pub fn in_progress(&self) -> bool {
match self {
ProcessingStatus::NotProcessed => false,
ProcessingStatus::Finished => false,
ProcessingStatus::Failed(_) => false,
ProcessingStatus::InProgress => true,
}
}
}
#[derive(Error, Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum FailedReason {
#[error("Collateral not available. Requested {requested}, but only available {available}")]
NotEnoughCollateral {
available: Collateral,
requested: NonZero<Collateral>,
},
#[error("Crank fee not available. Requested {requested}, but only available {available}")]
NotEnoughCrankFee {
available: Collateral,
requested: Collateral,
},
#[error("Collateral amount {funds} is less than chain's minimum representation.not available")]
FundLessThanMinChain {
funds: NonZero<Collateral>,
},
#[error("Shares not available. Requested {requested}, but only available {available}")]
NotEnoughShares {
available: LpToken,
requested: LpToken,
},
#[error("{market_id} result in error: {message}")]
MarketError {
market_id: MarketId,
message: String,
},
#[error("Deferred exec failure at {executed} because of: {reason}")]
DeferredExecFailure {
reason: String,
executed: Timestamp,
crank_price: Option<PricePoint>,
},
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum QueueItem {
IncCollaleteral {
item: IncQueueItem,
id: IncQueuePositionId,
},
DecCollateral {
item: Box<DecQueueItem>,
id: DecQueuePositionId,
},
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum IncQueueItem {
Deposit {
funds: NonZero<Collateral>,
token: Token,
},
MarketItem {
id: MarketId,
token: Token,
item: Box<IncMarketItem>,
},
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum DecQueueItem {
Withdrawal {
tokens: NonZero<LpToken>,
token: Token,
},
MarketItem {
id: MarketId,
token: Token,
item: Box<DecMarketItem>,
},
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum IncMarketItem {
UpdatePositionRemoveCollateralImpactLeverage {
id: PositionId,
amount: NonZero<Collateral>,
},
UpdatePositionRemoveCollateralImpactSize {
id: PositionId,
amount: NonZero<Collateral>,
slippage_assert: Option<SlippageAssert>,
},
CancelLimitOrder {
order_id: OrderId,
},
ClosePosition {
id: PositionId,
slippage_assert: Option<SlippageAssert>,
},
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum DecMarketItem {
OpenPosition {
collateral: NonZero<Collateral>,
slippage_assert: Option<SlippageAssert>,
leverage: LeverageToBase,
direction: DirectionToBase,
stop_loss_override: Option<PriceBaseInQuote>,
#[serde(alias = "take_profit_override")]
take_profit: TakeProfitTrader,
},
UpdatePositionAddCollateralImpactLeverage {
collateral: NonZero<Collateral>,
id: PositionId,
},
UpdatePositionAddCollateralImpactSize {
collateral: NonZero<Collateral>,
id: PositionId,
slippage_assert: Option<SlippageAssert>,
},
UpdatePositionLeverage {
id: PositionId,
leverage: LeverageToBase,
slippage_assert: Option<SlippageAssert>,
},
UpdatePositionTakeProfitPrice {
id: PositionId,
price: TakeProfitTrader,
},
UpdatePositionStopLossPrice {
id: PositionId,
stop_loss: StopLoss,
},
PlaceLimitOrder {
collateral: NonZero<Collateral>,
trigger_price: PriceBaseInQuote,
leverage: LeverageToBase,
direction: DirectionToBase,
stop_loss_override: Option<PriceBaseInQuote>,
#[serde(alias = "take_profit_override")]
take_profit: TakeProfitTrader,
},
}
pub enum RequiresToken {
Token {
token: Token,
},
NoToken {},
}
impl IncQueueItem {
pub fn requires_token(self) -> RequiresToken {
match self {
IncQueueItem::Deposit { token, .. } => RequiresToken::Token { token },
IncQueueItem::MarketItem { .. } => RequiresToken::NoToken {},
}
}
}
impl DecQueueItem {
pub fn requires_token(self) -> RequiresToken {
match self {
DecQueueItem::Withdrawal { token, .. } => RequiresToken::Token { token },
DecQueueItem::MarketItem { .. } => RequiresToken::NoToken {},
}
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct LeaderStatusResp {
pub tokens: Vec<TokenStatus>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct TokenStatus {
pub token: Token,
pub collateral: Collateral,
pub shares: LpToken,
pub unclaimed_commission: Collateral,
pub claimed_commission: Collateral,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct BalanceResp {
pub balance: Vec<BalanceRespItem>,
pub start_after: Option<Token>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct BalanceRespItem {
pub shares: NonZero<LpToken>,
pub token: Token,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Token {
Native(String),
Cw20(Addr),
}
impl Token {
pub fn is_same(&self, token: &crate::token::Token) -> bool {
match token {
crate::token::Token::Cw20 { addr, .. } => match self {
Token::Native(_) => false,
Token::Cw20(cw20_addr) => {
let cw20_addr: &RawAddr = &cw20_addr.into();
cw20_addr == addr
}
},
crate::token::Token::Native { denom, .. } => match self {
Token::Native(native_denom) => *native_denom == *denom,
Token::Cw20(_) => false,
},
}
}
}
impl<'a> PrimaryKey<'a> for Token {
type Prefix = ();
type SubPrefix = ();
type Suffix = Self;
type SuperSuffix = Self;
fn key(&self) -> Vec<Key> {
let (token_type, bytes) = match self {
Token::Native(native) => (0u8, native.as_bytes()),
Token::Cw20(cw20) => (1u8, cw20.as_bytes()),
};
let token_type = Key::Val8([token_type]);
let key = Key::Ref(bytes);
vec![token_type, key]
}
}
impl<'a> Prefixer<'a> for Token {
fn prefix(&self) -> Vec<Key> {
let (token_type, bytes) = match self {
Token::Native(native) => (0u8, native.as_bytes()),
Token::Cw20(cw20) => (1u8, cw20.as_bytes()),
};
let token_type = Key::Val8([token_type]);
let key = Key::Ref(bytes);
vec![token_type, key]
}
}
impl KeyDeserialize for Token {
type Output = Token;
const KEY_ELEMS: u16 = 2;
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
let (token_type, token) = <(u8, Vec<u8>) as KeyDeserialize>::from_vec(value)?;
let token = match token_type {
0 => {
let native_token = String::from_slice(&token)?;
Token::Native(native_token)
}
1 => {
let cw20_token = Addr::from_slice(&token)?;
Token::Cw20(cw20_token)
}
_ => {
return Err(StdError::serialize_err(
"Token",
"Invalid number in token_type",
))
}
};
Ok(token)
}
}
impl KeyDeserialize for &Token {
type Output = Token;
const KEY_ELEMS: u16 = 2;
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
let token = <Token as KeyDeserialize>::from_vec(value)?;
Ok(token)
}
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Token::Native(denom) => f.write_str(denom),
Token::Cw20(addr) => f.write_str(addr.as_str()),
}
}
}
impl Token {
pub fn ensure_matches(&self, token: &crate::token::Token) -> anyhow::Result<()> {
match (self, token) {
(Token::Native(_), crate::token::Token::Cw20 { addr, .. }) => {
anyhow::bail!("Provided native funds, but market requires a CW20 (contract {addr})")
}
(
Token::Native(denom1),
crate::token::Token::Native {
denom: denom2,
decimal_places: _,
},
) => {
if denom1 == denom2 {
Ok(())
} else {
Err(anyhow::anyhow!("Wrong denom provided. You sent {denom1}, but the contract expects {denom2}"))
}
}
(
Token::Cw20(addr1),
crate::token::Token::Cw20 {
addr: addr2,
decimal_places: _,
},
) => {
if addr1.as_str() == addr2.as_str() {
Ok(())
} else {
Err(anyhow::anyhow!(
"Wrong CW20 used. You used {addr1}, but the contract expects {addr2}"
))
}
}
(Token::Cw20(_), crate::token::Token::Native { denom, .. }) => {
anyhow::bail!(
"Provided CW20 funds, but market requires native funds with denom {denom}"
)
}
}
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum WorkResp {
NoWork,
HasWork {
work_description: WorkDescription,
},
}
impl WorkResp {
pub fn has_work(&self) -> bool {
match self {
WorkResp::NoWork => false,
WorkResp::HasWork { .. } => true,
}
}
pub fn is_deferred_work(&self) -> bool {
match self {
WorkResp::NoWork => false,
WorkResp::HasWork { work_description } => work_description.is_deferred_work(),
}
}
pub fn is_rebalance(&self) -> bool {
match self {
WorkResp::NoWork => false,
WorkResp::HasWork { work_description } => work_description.is_rebalance(),
}
}
pub fn is_compute_lp_token(&self) -> bool {
match self {
WorkResp::NoWork => false,
WorkResp::HasWork { work_description } => work_description.is_compute_lp_token(),
}
}
pub fn is_reset_status(&self) -> bool {
match self {
WorkResp::NoWork => false,
WorkResp::HasWork { work_description } => work_description.is_reset_status(),
}
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum WorkDescription {
LoadMarket {},
ComputeLpTokenValue {
token: Token,
process_start_from: Option<MarketId>,
validate_start_from: Option<MarketId>,
},
ProcessQueueItem {
id: QueuePositionId,
},
ResetStats {
token: Token,
},
HandleDeferredExecId {},
Rebalance {
token: Token,
amount: NonZero<Collateral>,
start_from: Option<MarketId>,
},
}
impl WorkDescription {
pub fn is_rebalance(&self) -> bool {
match self {
WorkDescription::LoadMarket {} => false,
WorkDescription::ComputeLpTokenValue { .. } => false,
WorkDescription::ProcessQueueItem { .. } => false,
WorkDescription::ResetStats { .. } => false,
WorkDescription::HandleDeferredExecId {} => false,
WorkDescription::Rebalance { .. } => true,
}
}
pub fn is_compute_lp_token(&self) -> bool {
match self {
WorkDescription::LoadMarket {} => false,
WorkDescription::ComputeLpTokenValue { .. } => true,
WorkDescription::ProcessQueueItem { .. } => false,
WorkDescription::ResetStats { .. } => false,
WorkDescription::HandleDeferredExecId {} => false,
WorkDescription::Rebalance { .. } => false,
}
}
pub fn is_reset_status(&self) -> bool {
match self {
WorkDescription::LoadMarket {} => false,
WorkDescription::ComputeLpTokenValue { .. } => false,
WorkDescription::ProcessQueueItem { .. } => false,
WorkDescription::ResetStats { .. } => true,
WorkDescription::HandleDeferredExecId {} => false,
WorkDescription::Rebalance { .. } => false,
}
}
pub fn is_deferred_work(&self) -> bool {
match self {
WorkDescription::LoadMarket {} => false,
WorkDescription::ComputeLpTokenValue { .. } => false,
WorkDescription::ProcessQueueItem { .. } => false,
WorkDescription::ResetStats { .. } => false,
WorkDescription::HandleDeferredExecId {} => true,
WorkDescription::Rebalance { .. } => false,
}
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum QueuePositionId {
IncQueuePositionId(IncQueuePositionId),
DecQueuePositionId(DecQueuePositionId),
}
impl<'a> PrimaryKey<'a> for QueuePositionId {
type Prefix = ();
type SubPrefix = ();
type Suffix = Self;
type SuperSuffix = Self;
fn key(&self) -> Vec<Key> {
let (queue_type, key) = match self {
QueuePositionId::IncQueuePositionId(id) => (0u8, id.key()),
QueuePositionId::DecQueuePositionId(id) => (1u8, id.key()),
};
let mut keys = vec![Key::Val8([queue_type])];
keys.extend(key);
keys
}
}
impl KeyDeserialize for QueuePositionId {
type Output = QueuePositionId;
const KEY_ELEMS: u16 = 2;
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
let (queue_type, queue_id) = <(u8, u64) as KeyDeserialize>::from_vec(value)?;
let position_id = match queue_type {
0 => QueuePositionId::IncQueuePositionId(IncQueuePositionId(queue_id.into())),
1 => QueuePositionId::DecQueuePositionId(DecQueuePositionId(queue_id.into())),
_ => {
return Err(StdError::serialize_err(
"QueuePositionId",
"Invalid number in queue_type",
))
}
};
Ok(position_id)
}
}
impl<'a> Prefixer<'a> for QueuePositionId {
fn prefix(&self) -> Vec<Key> {
match self {
QueuePositionId::IncQueuePositionId(id) => {
let mut keys = vec![Key::Val8([0u8])];
keys.extend(id.key());
keys
}
QueuePositionId::DecQueuePositionId(id) => {
let mut keys = vec![Key::Val8([1u8])];
keys.extend(id.key());
keys
}
}
}
}
#[derive(
Copy, PartialOrd, Ord, Eq, Clone, PartialEq, serde::Serialize, serde::Deserialize, Debug,
)]
#[serde(rename_all = "snake_case")]
pub struct IncQueuePositionId(Uint64);
impl IncQueuePositionId {
pub fn new(x: u64) -> Self {
IncQueuePositionId(x.into())
}
pub fn u64(self) -> u64 {
self.0.u64()
}
pub fn next(self) -> Self {
IncQueuePositionId((self.u64() + 1).into())
}
}
impl<'a> PrimaryKey<'a> for IncQueuePositionId {
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 IncQueuePositionId {
fn prefix(&self) -> Vec<Key> {
vec![Key::Val64(self.0.u64().to_cw_bytes())]
}
}
impl KeyDeserialize for IncQueuePositionId {
type Output = IncQueuePositionId;
const KEY_ELEMS: u16 = 1;
#[inline(always)]
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
u64::from_vec(value).map(|x| IncQueuePositionId(Uint64::new(x)))
}
}
impl std::fmt::Display for IncQueuePositionId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromStr for IncQueuePositionId {
type Err = ParseIntError;
fn from_str(src: &str) -> Result<Self, ParseIntError> {
src.parse().map(|x| IncQueuePositionId(Uint64::new(x)))
}
}
#[derive(
Copy, PartialOrd, Ord, Eq, Clone, PartialEq, serde::Serialize, serde::Deserialize, Debug,
)]
#[serde(rename_all = "snake_case")]
pub struct DecQueuePositionId(Uint64);
impl DecQueuePositionId {
pub fn new(x: u64) -> Self {
DecQueuePositionId(x.into())
}
pub fn u64(self) -> u64 {
self.0.u64()
}
pub fn next(self) -> Self {
DecQueuePositionId((self.u64() + 1).into())
}
}
impl<'a> PrimaryKey<'a> for DecQueuePositionId {
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 DecQueuePositionId {
fn prefix(&self) -> Vec<Key> {
vec![Key::Val64(self.0.u64().to_cw_bytes())]
}
}
impl KeyDeserialize for DecQueuePositionId {
type Output = DecQueuePositionId;
const KEY_ELEMS: u16 = 1;
#[inline(always)]
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
u64::from_vec(value).map(|x| DecQueuePositionId(Uint64::new(x)))
}
}
impl std::fmt::Display for DecQueuePositionId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl FromStr for DecQueuePositionId {
type Err = ParseIntError;
fn from_str(src: &str) -> Result<Self, ParseIntError> {
src.parse().map(|x| DecQueuePositionId(Uint64::new(x)))
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}