1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use cosmwasm_std::{Addr, BlockInfo, Env, Storage, Uint128};
5use cw_storage_plus::{Item, Map};
6
7use crate::ContractError;
8use cw20::{AllowanceResponse, Logo, MarketingInfoResponse};
9use wynd_utils::Curve;
10
11#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
12#[serde(rename_all = "snake_case")]
13pub struct TokenInfo {
14 pub name: String,
15 pub symbol: String,
16 pub decimals: u8,
17 pub total_supply: Uint128,
18 pub mint: Option<MinterData>,
19}
20
21#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
22pub struct MinterData {
23 pub minter: Addr,
24 pub cap: Option<Curve>,
26}
27
28impl TokenInfo {
29 pub fn get_cap(&self, block: &BlockInfo) -> Option<Uint128> {
30 self.mint
31 .as_ref()
32 .and_then(|v| v.cap.as_ref().map(|v| v.value(block.time.seconds())))
33 }
34}
35
36pub const ALLOWLIST: Item<Vec<Addr>> = Item::new("allowlist");
37pub const TOKEN_INFO: Item<TokenInfo> = Item::new("token_info");
38pub const MARKETING_INFO: Item<MarketingInfoResponse> = Item::new("marketing_info");
39pub const LOGO: Item<Logo> = Item::new("logo");
40pub const BALANCES: Map<&Addr, Uint128> = Map::new("balance");
41pub const ALLOWANCES: Map<(&Addr, &Addr), AllowanceResponse> = Map::new("allowance");
42pub const VESTING: Map<&Addr, Curve> = Map::new("vesting");
44
45pub fn deduct_coins(
49 storage: &mut dyn Storage,
50 env: &Env,
51 sender: &Addr,
52 amount: Uint128,
53) -> Result<Uint128, ContractError> {
54 let vesting = VESTING
56 .may_load(storage, sender)?
57 .map(|v| v.value(env.block.time.seconds()));
58
59 if vesting == Some(Uint128::zero()) {
62 VESTING.remove(storage, sender);
63 }
64
65 BALANCES.update(storage, sender, |balance: Option<Uint128>| {
66 let remainder = balance.unwrap_or_default().checked_sub(amount)?;
67 if let Some(vest) = vesting {
69 if vest > remainder {
70 return Err(ContractError::CantMoveVestingTokens);
71 }
72 }
73 Ok(remainder)
74 })
75}