white_whale_std/pool_network/
incentive.rs

1use std::collections::{BTreeMap, HashMap};
2use std::fmt;
3
4use cosmwasm_schema::{cw_serde, QueryResponses};
5use cosmwasm_std::{Addr, Decimal256, Uint128};
6
7use crate::pool_network::asset::{Asset, AssetInfo};
8
9#[cw_serde]
10pub struct InstantiateMsg {
11    /// The address of the LP token that the incentive should be tied to.
12    pub lp_asset: AssetInfo,
13    /// Fee distributor contract address.
14    pub fee_distributor_address: String,
15}
16
17#[cw_serde]
18pub enum ExecuteMsg {
19    /// Makes a snapshot of the current global weight, at the current epoch.
20    TakeGlobalWeightSnapshot {},
21    /// Opens a new liquidity flow
22    OpenFlow {
23        /// The epoch at which the flow will start. If unspecified, the flow will start at the
24        /// current epoch.
25        start_epoch: Option<u64>,
26        /// The epoch at which the flow should end. If unspecified, the flow will default to end at
27        /// 14 epochs from the current one.
28        end_epoch: Option<u64>,
29        /// The type of distribution curve. If unspecified, the distribution will be linear.
30        curve: Option<Curve>,
31        /// The asset to be distributed in this flow.
32        flow_asset: Asset,
33        /// If set, the label will be used to identify the flow, in addition to the flow_id.
34        flow_label: Option<String>,
35    },
36    /// Closes an existing liquidity flow.
37    ///
38    /// Sender of the message must either be the contract admin or the creator of the flow.
39    CloseFlow {
40        /// The identifier of the flow to close.
41        flow_identifier: FlowIdentifier,
42    },
43    /// Creates a new position to earn flow rewards.
44    OpenPosition {
45        /// The amount to add to the position.
46        amount: Uint128,
47        /// The amount of time (in seconds) before the LP tokens can be redeemed.
48        unbonding_duration: u64,
49        /// The receiver of the new position.
50        ///
51        /// This is mostly used for the frontend helper contract.
52        ///
53        /// If left empty, defaults to the message sender.
54        receiver: Option<String>,
55    },
56    /// Expands an existing position to earn more flow rewards.
57    ExpandPosition {
58        /// The amount to add to the existing position.
59        amount: Uint128,
60        /// The unbond completion timestamp to identify the position to add to.
61        unbonding_duration: u64,
62        /// The receiver of the expanded position.
63        ///
64        /// This is mostly used for the frontend helper contract.
65        ///
66        /// If left empty, defaults to the message sender.
67        receiver: Option<String>,
68    },
69    /// Closes an existing position to stop earning flow rewards.
70    ClosePosition {
71        /// The unbonding duration of the position to close.
72        unbonding_duration: u64,
73    },
74    /// Withdraws the LP tokens from a closed position once the unbonding duration has passed.
75    Withdraw {},
76    /// Claims the flow rewards.
77    Claim {},
78    /// Expands an existing flow.
79    ExpandFlow {
80        /// The identifier of the flow to expand, whether an id or a label.
81        flow_identifier: FlowIdentifier,
82        /// The epoch at which the flow should end. If not set, the flow will be expanded a default value of 14 epochs.
83        end_epoch: Option<u64>,
84        /// The asset to expand this flow with.
85        flow_asset: Asset,
86    },
87}
88
89#[cw_serde]
90pub struct MigrateMsg {}
91
92/// Represents a flow.
93#[cw_serde]
94pub struct Flow {
95    /// A unique identifier of the flow.
96    pub flow_id: u64,
97    /// An alternative flow label.
98    pub flow_label: Option<String>,
99    /// The account which opened the flow and can manage it.
100    pub flow_creator: Addr,
101    /// The asset the flow was created to distribute.
102    pub flow_asset: Asset,
103    /// The amount of the `flow_asset` that has been claimed so far.
104    pub claimed_amount: Uint128,
105    /// The type of curve the flow has.
106    pub curve: Curve,
107    //todo not doing anything for now
108    /// The epoch at which the flow starts.
109    pub start_epoch: u64,
110    /// The epoch at which the flow ends.
111    pub end_epoch: u64,
112    /// emitted tokens
113    pub emitted_tokens: HashMap<u64, Uint128>,
114    /// A map containing the amount of tokens it was expanded to at a given epoch. This is used
115    /// to calculate the right amount of tokens to distribute at a given epoch when a flow is expanded.
116    pub asset_history: BTreeMap<u64, (Uint128, u64)>,
117}
118
119/// Represents a position that accumulates flow rewards.
120///
121/// An address can have multiple incentive positions active at once.
122#[cw_serde]
123pub struct OpenPosition {
124    /// The amount of LP tokens that are put up to earn incentives.
125    pub amount: Uint128,
126    /// Represents the amount of time in seconds the user must wait after unbonding for the LP tokens to be released.
127    pub unbonding_duration: u64,
128}
129
130impl fmt::Display for OpenPosition {
131    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132        write!(
133            f,
134            "OpenPosition(amount: {}, unbonding_duration: {})",
135            self.amount, self.unbonding_duration
136        )
137    }
138}
139
140/// Represents a position that has moved from the [`OpenPosition`] state.
141///
142/// This position is no longer accumulating rewards, and the underlying tokens are claimable after `unbonding_duration`.
143#[cw_serde]
144pub struct ClosedPosition {
145    /// The amount of LP tokens that the user is unbonding in this position.
146    pub amount: Uint128,
147    /// The block timestamp when the user can withdraw the position to retrieve the underlying `amount` of LP tokens.
148    pub unbonding_timestamp: u64,
149}
150
151#[cw_serde]
152#[derive(QueryResponses)]
153pub enum QueryMsg {
154    /// Retrieves the current contract configuration.
155    #[returns(ConfigResponse)]
156    Config {},
157    /// Retrieves a specific flow. If start_epoch and end_epoch are set, the asset_history and
158    /// emitted_tokens will be filtered to only include epochs within the range. The maximum gap between
159    /// the start_epoch and end_epoch is 100 epochs.
160    #[returns(FlowResponse)]
161    Flow {
162        /// The id of the flow to find.
163        flow_identifier: FlowIdentifier,
164        /// If set, filters the asset_history and emitted_tokens to only include epochs from start_epoch.
165        start_epoch: Option<u64>,
166        /// If set, filters the asset_history and emitted_tokens to only include epochs until end_epoch.
167        end_epoch: Option<u64>,
168    },
169    /// Retrieves the current flows. If start_epoch and end_epoch are set, the asset_history and
170    /// emitted_tokens will be filtered to only include epochs within the range. The maximum gap between
171    /// the start_epoch and end_epoch is 100 epochs.
172    #[returns(FlowsResponse)]
173    Flows {
174        /// If set, filters the asset_history and emitted_tokens to only include epochs from start_epoch.
175        start_epoch: Option<u64>,
176        /// If set, filters the asset_history and emitted_tokens to only include epochs until end_epoch.
177        end_epoch: Option<u64>,
178    },
179    /// Retrieves the positions for an address.
180    #[returns(PositionsResponse)]
181    Positions {
182        /// The address to get positions for.
183        address: String,
184    },
185    /// Retrieves the rewards for an address.
186    #[returns(RewardsResponse)]
187    Rewards {
188        /// The address to get all the incentive rewards for.
189        address: String,
190    },
191    /// Retrieves the rewards for an address.
192    #[returns(GlobalWeightResponse)]
193    GlobalWeight {
194        /// The epoch to get the global weight for.
195        epoch_id: u64,
196    },
197    /// Retrieves the rewards/weight share of an address for the current epoch.
198    #[returns(RewardsShareResponse)]
199    CurrentEpochRewardsShare {
200        /// The address to query the rewards share for.
201        address: String,
202    },
203}
204
205/// Stores the reply data set in the response when instantiating an incentive contract.
206#[cw_serde]
207pub struct InstantiateReplyCallback {
208    /// The address of the LP token that is tied to the incentive contract.
209    pub lp_asset: AssetInfo,
210}
211
212/// Represents the configuration of the incentive contract.
213#[cw_serde]
214pub struct Config {
215    /// The address of the incentive factory.
216    pub factory_address: Addr,
217
218    /// Fee distributor contract.
219    pub fee_distributor_address: Addr,
220
221    /// The LP token asset tied to the incentive contract.
222    pub lp_asset: AssetInfo,
223}
224
225/// The type of distribution curve to exist.
226#[cw_serde]
227pub enum Curve {
228    /// A linear curve that releases assets as we approach the end of the flow period.
229    Linear,
230}
231
232impl std::fmt::Display for Curve {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        match self {
235            Curve::Linear => write!(f, "Linear"),
236        }
237    }
238}
239
240pub type ConfigResponse = Config;
241
242#[cw_serde]
243pub struct FlowResponse {
244    //TODO why is this returning a Option<Flow>? why not a flow directly?
245    /// The flow that was searched for.
246    pub flow: Option<Flow>,
247}
248
249#[cw_serde]
250pub struct FlowsResponse {
251    /// The current flows.
252    pub flows: Vec<Flow>,
253}
254
255#[cw_serde]
256pub enum QueryPosition {
257    /// Represents a position that a user has deposited, but not yet begun to unbond.
258    OpenPosition {
259        /// The amount of LP tokens the user deposited into the position.
260        amount: Uint128,
261        /// The amount of time (in seconds) the user must wait after they begin the unbonding process.
262        unbonding_duration: u64,
263        /// The amount of weight the position has.
264        weight: Uint128,
265    },
266    /// Represents a position that a user has initiated the unbonding process on. The position may or may not be withdrawable.
267    ClosedPosition {
268        /// The amount of LP tokens the user deposited into the position, and will receive after they withdraw.
269        amount: Uint128,
270        /// The timestamp (in seconds) the user unbonded at.
271        unbonding_timestamp: u64,
272        /// The amount of weight the position has.
273        weight: Uint128,
274    },
275}
276
277#[cw_serde]
278pub struct PositionsResponse {
279    /// The current time of the blockchain.
280    pub timestamp: u64,
281    /// All the positions a user has.
282    pub positions: Vec<QueryPosition>,
283}
284
285#[cw_serde]
286pub struct RewardsResponse {
287    /// The rewards that is available to a user if they executed the `claim` function at this point.
288    pub rewards: Vec<Asset>,
289}
290
291#[cw_serde]
292pub struct GlobalWeightResponse {
293    /// the global weight of the incentive contract for the given epoch
294    pub global_weight: Uint128,
295    /// Epoch id for which the global weight is calculated
296    pub epoch_id: u64,
297}
298
299#[cw_serde]
300pub struct RewardsShareResponse {
301    pub address: Addr,
302    pub global_weight: Uint128,
303    pub address_weight: Uint128,
304    pub share: Decimal256,
305    pub epoch_id: u64,
306}
307
308#[cw_serde]
309pub enum FlowIdentifier {
310    Id(u64),
311    Label(String),
312}
313
314impl fmt::Display for FlowIdentifier {
315    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316        match self {
317            FlowIdentifier::Id(flow_id) => write!(f, "flow_id: {}", flow_id),
318            FlowIdentifier::Label(flow_label) => write!(f, "flow_label: {}", flow_label),
319        }
320    }
321}