use std::collections::BTreeMap;
use cosmwasm_std::{BankQuery, Coin, DepsMut, QueryRequest, StdError, SupplyResponse, Timestamp, Uint128};
use injective_cosmwasm::InjectiveQueryWrapper;
use injective_math::FPDecimal;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use wynd_utils::Curve;
use crate::{
vault::{AmmInstantiateMsg, InstantiateVaultMsg},
vesting_errors::ContractError,
};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct FactoryInstantiateMsg {
pub owner: String,
pub launchpad_code_id: u64,
pub master_address: String,
pub mito_treasury_address: String,
pub default_staked_to_subscription: BTreeMap<Uint128, Uint128>,
pub launch_fee: Coin,
pub launchpads_quote_denom: String,
pub quote_decimals: u8,
pub mito_treasury_fee_basis_points: u16,
pub vesting_code_id: u64,
}
pub fn validate_denom(denom: &str, deps: &DepsMut<InjectiveQueryWrapper>) -> Result<(), StdError> {
if denom.is_empty() {
return Err(StdError::generic_err("denom cannot be empty".to_string()));
}
let supply: SupplyResponse = deps.querier.query(&QueryRequest::Bank(BankQuery::Supply { denom: denom.to_string() }))?;
if supply.amount.amount.is_zero() {
return Err(StdError::generic_err(format!("denom {} doesn't exist or has 0 supply", denom)));
}
Ok(())
}
pub fn validate_staked_to_subscription(staked_to_subscription: &BTreeMap<Uint128, Uint128>) -> Result<(), StdError> {
if staked_to_subscription.is_empty() {
return Err(StdError::generic_err(
"'staked_to_subscription' must contain at least one key-value pair".to_string(),
));
}
let mut previous_subscription_allowance = Uint128::zero();
staked_to_subscription.iter().try_for_each(|(_, subscription_allowance)| {
if subscription_allowance <= &previous_subscription_allowance {
return Err(StdError::generic_err(
"subscription allowance must be greater than previous allowance".to_string(),
));
}
previous_subscription_allowance = subscription_allowance.to_owned();
Ok(())
})?;
Ok(())
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct AmmLaunchConfig {
pub msg: Box<AmmInstantiateMsg>,
pub code_id: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum FactoryExecuteMsg {
RegisterVaultInMaster {
instantiate_vault_msg: Box<InstantiateVaultMsg>,
vault_code_id: u64,
vault_label: String,
},
UpdateConfig {
owner: Option<String>,
launchpad_code_id: Option<u64>,
vesting_code_id: Option<u64>,
launch_fee: Option<Coin>,
master_address: Option<String>,
mito_treasury_address: Option<String>,
mito_treasury_fee_basis_points: Option<u16>,
default_staked_to_subscription: Option<BTreeMap<Uint128, Uint128>>,
launchpads_quote_denom: Option<String>,
quote_decimals: Option<u8>,
},
DeployLaunchpad {
project_owner: String,
project_token_denom: String,
hard_cap: Option<Uint128>,
use_whitelist: bool,
target_usd_subscription_incl_vault: FPDecimal,
quote_price_set_once_before_start_in_seconds: u64,
quote_pyth_pricefeed_id: String,
oracle_stale_time: u64,
vesting_config: Option<FactoryVestingConfig>,
amm_launch_config: Option<AmmLaunchConfig>,
staked_to_subscription_override: Option<BTreeMap<Uint128, Uint128>>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum Schedule {
SaturatingLinear,
PiecewiseLinear(Vec<(u64, Uint128)>),
}
impl Schedule {
pub fn into_curve(self, total: Uint128, duration_seconds: u64) -> Result<Curve, ContractError> {
let c = match self {
Schedule::SaturatingLinear => Curve::saturating_linear((0, 0), (duration_seconds, total.u128())),
Schedule::PiecewiseLinear(steps) => {
if steps.len() < 2 {
return Err(ContractError::ConstantVest);
}
Curve::PiecewiseLinear(wynd_utils::PiecewiseLinear { steps })
}
};
c.validate_monotonic_increasing()?; let range = c.range();
if range != (0, total.u128()) {
return Err(ContractError::VestRange {
min: Uint128::new(range.0),
max: Uint128::new(range.1),
});
}
Ok(c)
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum RelativeSchedule {
SaturatingLinear,
PiecewiseLinear(Vec<(u64, u16)>),
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct VestingPlan {
pub vesting_duration_seconds: u64,
pub schedule: RelativeSchedule,
pub vesting_start_delay_seconds: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct FactoryVestingConfig {
pub project_owner_quote: Option<VestingPlan>,
pub project_owner_lp_tokens: Option<VestingPlan>,
pub users_project_token: Option<VestingPlan>,
}
impl FactoryVestingConfig {
pub fn into_vesting_config(self, vesting_code_id: u64) -> VestingConfig {
VestingConfig {
project_owner_quote: self.project_owner_quote,
project_owner_lp_tokens: self.project_owner_lp_tokens,
users_project_token: self.users_project_token,
code_id: vesting_code_id,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct VestingConfig {
pub project_owner_quote: Option<VestingPlan>,
pub project_owner_lp_tokens: Option<VestingPlan>,
pub users_project_token: Option<VestingPlan>,
pub code_id: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct InstantiateMsg {
pub project_owner: String,
pub project_token_denom: String,
pub quote_denom: String,
pub hard_cap: Option<Uint128>,
pub amm_launch_config: Option<AmmLaunchConfig>,
pub vesting_config: Option<VestingConfig>,
pub use_whitelist: bool,
pub staked_to_subscription: BTreeMap<Uint128, Uint128>,
pub mito_treasury_address: String,
pub master_address: String,
pub target_usd_subscription_incl_vault: FPDecimal,
pub quote_price_set_once_before_start_in_seconds: u64,
pub quote_pyth_pricefeed_id: String,
pub oracle_stale_time: u64,
pub quote_decimals: u8,
pub mito_treasury_fee_basis_points: u16,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct MultipliedAddress {
pub address: String,
pub weight: FPDecimal,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum ExecuteMsg {
UpdateOwner {
new_owner: String,
},
AddAddressesToWhitelist {
addresses: Vec<MultipliedAddress>,
},
RemoveAddressesFromWhitelist {
addresses: Vec<String>,
should_revoke_subscription: bool,
},
StartIdo {
start_timestamp: Timestamp,
end_timestamp: Timestamp,
},
CancelIdo {},
SetQuotePrice {},
Subscribe {},
Finalize {},
Claim {},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct MigrateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct FactoryMigrateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum FactoryQueryMsg {
Config {},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum LaunchpadQueryMsg {
Config {},
StakedAmount { address: String, max_delegations: u16 },
MaxSubscriptionAmount { address: String },
CurrentSubscriptionAmount { address: String },
TotalSubscriptionAmount {},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct IdoTimestamps {
pub start: Timestamp,
pub end: Timestamp,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct LaunchpadConfigResponse {
pub project_owner: String,
pub ido_timestamps: Option<IdoTimestamps>,
pub hard_cap: Option<Uint128>,
pub use_whitelist: bool,
pub amm_launch_config: Option<AmmLaunchConfig>,
pub vesting_config: Option<VestingConfig>,
pub mito_treasury_address: String,
pub master_address: String,
pub target_usd_subscription_incl_vault: FPDecimal,
pub staked_to_subscription: BTreeMap<Uint128, Uint128>,
pub quote_price_set_once_before_start_in_seconds: u64,
pub quote_pyth_pricefeed_id: String,
pub quote_decimals: u8,
pub oracle_stale_time: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct LaunchpadAmountResponse {
pub amount: Uint128,
}