apollo_cw_multi_test/
staking.rs

1use std::collections::{BTreeSet, HashMap, VecDeque};
2
3use anyhow::{anyhow, bail, Result as AnyResult};
4use schemars::JsonSchema;
5
6use cosmwasm_std::{
7    coin, ensure, ensure_eq, to_binary, Addr, AllDelegationsResponse, AllValidatorsResponse, Api,
8    BankMsg, Binary, BlockInfo, BondedDenomResponse, Coin, CustomQuery, Decimal, Delegation,
9    DelegationResponse, DistributionMsg, Empty, Event, FullDelegation, Querier, StakingMsg,
10    StakingQuery, Storage, Timestamp, Uint128, Validator, ValidatorResponse,
11};
12use cw_storage_plus::{Deque, Item, Map};
13use serde::{Deserialize, Serialize};
14
15use crate::app::CosmosRouter;
16use crate::executor::AppResponse;
17use crate::prefixed_storage::{prefixed, prefixed_read};
18use crate::{BankSudo, Module};
19
20// Contains some general staking parameters
21#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
22pub struct StakingInfo {
23    /// The denominator of the staking token
24    pub bonded_denom: String,
25    /// Time between unbonding and receiving tokens in seconds
26    pub unbonding_time: u64,
27    /// Interest rate per year (60 * 60 * 24 * 365 seconds)
28    pub apr: Decimal,
29}
30
31impl Default for StakingInfo {
32    fn default() -> Self {
33        StakingInfo {
34            bonded_denom: "TOKEN".to_string(),
35            unbonding_time: 60,
36            apr: Decimal::percent(10),
37        }
38    }
39}
40
41/// The number of stake and rewards of this validator the staker has. These can be fractional in case of slashing.
42#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, JsonSchema)]
43struct Shares {
44    stake: Decimal,
45    rewards: Decimal,
46}
47
48impl Shares {
49    /// Calculates the share of validator rewards that should be given to this staker.
50    pub fn share_of_rewards(&self, validator: &ValidatorInfo, rewards: Decimal) -> Decimal {
51        if validator.stake.is_zero() {
52            return Decimal::zero();
53        }
54        rewards * self.stake / validator.stake
55    }
56}
57
58/// Holds some operational data about a validator
59#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
60struct ValidatorInfo {
61    /// The stakers that have staked with this validator.
62    /// We need to track them for updating their rewards.
63    stakers: BTreeSet<Addr>,
64    /// The whole stake of all stakers
65    stake: Uint128,
66    /// The block time when this validator's rewards were last update. This is needed for rewards calculation.
67    last_rewards_calculation: Timestamp,
68}
69
70impl ValidatorInfo {
71    pub fn new(block_time: Timestamp) -> Self {
72        Self {
73            stakers: BTreeSet::new(),
74            stake: Uint128::zero(),
75            last_rewards_calculation: block_time,
76        }
77    }
78}
79
80#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
81struct Unbonding {
82    pub delegator: Addr,
83    pub validator: Addr,
84    pub amount: Uint128,
85    pub payout_at: Timestamp,
86}
87
88const STAKING_INFO: Item<StakingInfo> = Item::new("staking_info");
89/// (staker_addr, validator_addr) -> shares
90const STAKES: Map<(&Addr, &Addr), Shares> = Map::new("stakes");
91const VALIDATOR_MAP: Map<&Addr, Validator> = Map::new("validator_map");
92/// Additional vec of validators, in case the `iterator` feature is disabled
93const VALIDATORS: Deque<Validator> = Deque::new("validators");
94/// Contains additional info for each validator
95const VALIDATOR_INFO: Map<&Addr, ValidatorInfo> = Map::new("validator_info");
96/// The queue of unbonding operations. This is needed because unbonding has a waiting time. See [`StakeKeeper`]
97const UNBONDING_QUEUE: Item<VecDeque<Unbonding>> = Item::new("unbonding_queue");
98/// (addr) -> addr. Maps addresses to the address they have delegated
99/// to receive their staking rewards. A missing key => no delegation
100/// has been set.
101const WITHDRAW_ADDRESS: Map<&Addr, Addr> = Map::new("withdraw_address");
102
103pub const NAMESPACE_STAKING: &[u8] = b"staking";
104// https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/types/keys.go#L16
105pub const NAMESPACE_DISTRIBUTION: &[u8] = b"distribution";
106
107// We need to expand on this, but we will need this to properly test out staking
108#[derive(Clone, std::fmt::Debug, PartialEq, Eq, JsonSchema)]
109pub enum StakingSudo {
110    /// Slashes the given percentage of the validator's stake.
111    /// For now, you cannot slash retrospectively in tests.
112    Slash {
113        validator: String,
114        percentage: Decimal,
115    },
116    /// Causes the unbonding queue to be processed.
117    /// This needs to be triggered manually, since there is no good place to do this right now.
118    /// In cosmos-sdk, this is done in `EndBlock`, but we don't have that here.
119    ProcessQueue {},
120}
121
122pub trait Staking: Module<ExecT = StakingMsg, QueryT = StakingQuery, SudoT = StakingSudo> {}
123
124pub trait Distribution: Module<ExecT = DistributionMsg, QueryT = Empty, SudoT = Empty> {}
125
126pub struct StakeKeeper {
127    module_addr: Addr,
128}
129
130impl Default for StakeKeeper {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl StakeKeeper {
137    pub fn new() -> Self {
138        StakeKeeper {
139            // The address of the staking module. This holds all staked tokens.
140            module_addr: Addr::unchecked("staking_module"),
141        }
142    }
143
144    /// Provides some general parameters to the stake keeper
145    pub fn setup(&self, storage: &mut dyn Storage, staking_info: StakingInfo) -> AnyResult<()> {
146        let mut storage = prefixed(storage, NAMESPACE_STAKING);
147
148        STAKING_INFO.save(&mut storage, &staking_info)?;
149        Ok(())
150    }
151
152    /// Add a new validator available for staking
153    pub fn add_validator(
154        &self,
155        api: &dyn Api,
156        storage: &mut dyn Storage,
157        block: &BlockInfo,
158        validator: Validator,
159    ) -> AnyResult<()> {
160        let mut storage = prefixed(storage, NAMESPACE_STAKING);
161
162        let val_addr = api.addr_validate(&validator.address)?;
163        if VALIDATOR_MAP.may_load(&storage, &val_addr)?.is_some() {
164            bail!(
165                "Cannot add validator {}, since a validator with that address already exists",
166                val_addr
167            );
168        }
169
170        VALIDATOR_MAP.save(&mut storage, &val_addr, &validator)?;
171        VALIDATORS.push_back(&mut storage, &validator)?;
172        VALIDATOR_INFO.save(&mut storage, &val_addr, &ValidatorInfo::new(block.time))?;
173        Ok(())
174    }
175
176    fn get_staking_info(staking_storage: &dyn Storage) -> AnyResult<StakingInfo> {
177        Ok(STAKING_INFO.may_load(staking_storage)?.unwrap_or_default())
178    }
179
180    /// Returns the rewards of the given delegator at the given validator
181    pub fn get_rewards(
182        &self,
183        storage: &dyn Storage,
184        block: &BlockInfo,
185        delegator: &Addr,
186        validator: &Addr,
187    ) -> AnyResult<Option<Coin>> {
188        let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
189
190        let validator_obj = match self.get_validator(&staking_storage, validator)? {
191            Some(validator) => validator,
192            None => bail!("validator {} not found", validator),
193        };
194        // calculate rewards using fixed ratio
195        let shares = match STAKES.load(&staking_storage, (delegator, validator)) {
196            Ok(stakes) => stakes,
197            Err(_) => {
198                return Ok(None);
199            }
200        };
201        let validator_info = VALIDATOR_INFO.load(&staking_storage, validator)?;
202
203        Self::get_rewards_internal(
204            &staking_storage,
205            block,
206            &shares,
207            &validator_obj,
208            &validator_info,
209        )
210        .map(Some)
211    }
212
213    fn get_rewards_internal(
214        staking_storage: &dyn Storage,
215        block: &BlockInfo,
216        shares: &Shares,
217        validator: &Validator,
218        validator_info: &ValidatorInfo,
219    ) -> AnyResult<Coin> {
220        let staking_info = Self::get_staking_info(staking_storage)?;
221
222        // calculate missing rewards without updating the validator to reduce rounding errors
223        let new_validator_rewards = Self::calculate_rewards(
224            block.time,
225            validator_info.last_rewards_calculation,
226            staking_info.apr,
227            validator.commission,
228            validator_info.stake,
229        );
230
231        // calculate the delegator's share of those
232        let delegator_rewards =
233            shares.rewards + shares.share_of_rewards(validator_info, new_validator_rewards);
234
235        Ok(Coin {
236            denom: staking_info.bonded_denom,
237            amount: Uint128::new(1) * delegator_rewards, // multiplying by 1 to convert Decimal to Uint128
238        })
239    }
240
241    /// Calculates the rewards that are due since the last calculation.
242    fn calculate_rewards(
243        current_time: Timestamp,
244        since: Timestamp,
245        interest_rate: Decimal,
246        validator_commission: Decimal,
247        stake: Uint128,
248    ) -> Decimal {
249        // calculate time since last update (in seconds)
250        let time_diff = current_time.minus_seconds(since.seconds()).seconds();
251
252        // using decimal here to reduce rounding error when calling this function a lot
253        let reward = Decimal::from_ratio(stake, 1u128)
254            * interest_rate
255            * Decimal::from_ratio(time_diff, 1u128)
256            / Decimal::from_ratio(60u128 * 60 * 24 * 365, 1u128);
257        let commission = reward * validator_commission;
258
259        reward - commission
260    }
261
262    /// Updates the staking reward for the given validator and their stakers
263    /// It saves the validator info and it's stakers, so make sure not to overwrite that.
264    /// Always call this to update rewards before changing anything that influences future rewards.
265    fn update_rewards(
266        api: &dyn Api,
267        staking_storage: &mut dyn Storage,
268        block: &BlockInfo,
269        validator: &Addr,
270    ) -> AnyResult<()> {
271        let staking_info = Self::get_staking_info(staking_storage)?;
272
273        let mut validator_info = VALIDATOR_INFO
274            .may_load(staking_storage, validator)?
275            .ok_or_else(|| anyhow!("validator {} not found", validator))?;
276
277        let validator_obj = VALIDATOR_MAP.load(staking_storage, validator)?;
278
279        if validator_info.last_rewards_calculation >= block.time {
280            return Ok(());
281        }
282
283        let new_rewards = Self::calculate_rewards(
284            block.time,
285            validator_info.last_rewards_calculation,
286            staking_info.apr,
287            validator_obj.commission,
288            validator_info.stake,
289        );
290
291        // update validator info and delegators
292        if !new_rewards.is_zero() {
293            validator_info.last_rewards_calculation = block.time;
294
295            // save updated validator
296            VALIDATOR_INFO.save(staking_storage, validator, &validator_info)?;
297
298            let validator_addr = api.addr_validate(&validator_obj.address)?;
299            // update all delegators
300            for staker in validator_info.stakers.iter() {
301                STAKES.update(
302                    staking_storage,
303                    (staker, &validator_addr),
304                    |shares| -> AnyResult<_> {
305                        let mut shares =
306                            shares.expect("all stakers in validator_info should exist");
307                        shares.rewards += shares.share_of_rewards(&validator_info, new_rewards);
308                        Ok(shares)
309                    },
310                )?;
311            }
312        }
313        Ok(())
314    }
315
316    /// Returns the single validator with the given address (or `None` if there is no such validator)
317    fn get_validator(
318        &self,
319        staking_storage: &dyn Storage,
320        address: &Addr,
321    ) -> AnyResult<Option<Validator>> {
322        Ok(VALIDATOR_MAP.may_load(staking_storage, address)?)
323    }
324
325    /// Returns all available validators
326    fn get_validators(&self, staking_storage: &dyn Storage) -> AnyResult<Vec<Validator>> {
327        let res: Result<_, _> = VALIDATORS.iter(staking_storage)?.collect();
328        Ok(res?)
329    }
330
331    fn get_stake(
332        &self,
333        staking_storage: &dyn Storage,
334        account: &Addr,
335        validator: &Addr,
336    ) -> AnyResult<Option<Coin>> {
337        let shares = STAKES.may_load(staking_storage, (account, validator))?;
338        let staking_info = Self::get_staking_info(staking_storage)?;
339
340        Ok(shares.map(|shares| {
341            Coin {
342                denom: staking_info.bonded_denom,
343                amount: Uint128::new(1) * shares.stake, // multiplying by 1 to convert Decimal to Uint128
344            }
345        }))
346    }
347
348    fn add_stake(
349        &self,
350        api: &dyn Api,
351        staking_storage: &mut dyn Storage,
352        block: &BlockInfo,
353        to_address: &Addr,
354        validator: &Addr,
355        amount: Coin,
356    ) -> AnyResult<()> {
357        self.validate_denom(staking_storage, &amount)?;
358        self.update_stake(
359            api,
360            staking_storage,
361            block,
362            to_address,
363            validator,
364            amount.amount,
365            false,
366        )
367    }
368
369    fn remove_stake(
370        &self,
371        api: &dyn Api,
372        staking_storage: &mut dyn Storage,
373        block: &BlockInfo,
374        from_address: &Addr,
375        validator: &Addr,
376        amount: Coin,
377    ) -> AnyResult<()> {
378        self.validate_denom(staking_storage, &amount)?;
379        self.update_stake(
380            api,
381            staking_storage,
382            block,
383            from_address,
384            validator,
385            amount.amount,
386            true,
387        )
388    }
389
390    fn update_stake(
391        &self,
392        api: &dyn Api,
393        staking_storage: &mut dyn Storage,
394        block: &BlockInfo,
395        delegator: &Addr,
396        validator: &Addr,
397        amount: impl Into<Uint128>,
398        sub: bool,
399    ) -> AnyResult<()> {
400        let amount = amount.into();
401
402        // update rewards for this validator
403        Self::update_rewards(api, staking_storage, block, validator)?;
404
405        // now, we can update the stake of the delegator and validator
406        let mut validator_info = VALIDATOR_INFO
407            .may_load(staking_storage, validator)?
408            .unwrap_or_else(|| ValidatorInfo::new(block.time));
409        let mut shares = STAKES
410            .may_load(staking_storage, (delegator, validator))?
411            .unwrap_or_default();
412        let amount_dec = Decimal::from_ratio(amount, 1u128);
413        if sub {
414            if amount_dec > shares.stake {
415                bail!("insufficient stake");
416            }
417            shares.stake -= amount_dec;
418            validator_info.stake = validator_info.stake.checked_sub(amount)?;
419        } else {
420            shares.stake += amount_dec;
421            validator_info.stake = validator_info.stake.checked_add(amount)?;
422        }
423
424        // check if any unbonding stake is left
425        let unbonding = UNBONDING_QUEUE
426            .may_load(staking_storage)?
427            .unwrap_or_default()
428            .into_iter()
429            .filter(|ub| &ub.delegator == delegator && &ub.validator == validator)
430            .map(|ub| ub.amount)
431            .sum::<Uint128>();
432
433        // save updated values
434        if shares.stake.is_zero() && unbonding.is_zero() {
435            // no more stake, so remove
436            STAKES.remove(staking_storage, (delegator, validator));
437            validator_info.stakers.remove(delegator);
438        } else {
439            STAKES.save(staking_storage, (delegator, validator), &shares)?;
440            validator_info.stakers.insert(delegator.clone());
441        }
442        // save updated validator info
443        VALIDATOR_INFO.save(staking_storage, validator, &validator_info)?;
444
445        Ok(())
446    }
447
448    fn slash(
449        &self,
450        api: &dyn Api,
451        staking_storage: &mut dyn Storage,
452        block: &BlockInfo,
453        validator: &Addr,
454        percentage: Decimal,
455    ) -> AnyResult<()> {
456        // calculate rewards before slashing
457        Self::update_rewards(api, staking_storage, block, validator)?;
458
459        // update stake of validator and stakers
460        let mut validator_info = VALIDATOR_INFO
461            .may_load(staking_storage, validator)?
462            .ok_or_else(|| anyhow!("validator {} not found", validator))?;
463
464        let remaining_percentage = Decimal::one() - percentage;
465        validator_info.stake = validator_info.stake * remaining_percentage;
466
467        // if the stake is completely gone, we clear all stakers and reinitialize the validator
468        if validator_info.stake.is_zero() {
469            // need to remove all stakes
470            for delegator in validator_info.stakers.iter() {
471                STAKES.remove(staking_storage, (delegator, validator));
472            }
473            validator_info.stakers.clear();
474        } else {
475            // otherwise we update all stakers
476            for delegator in validator_info.stakers.iter() {
477                STAKES.update(
478                    staking_storage,
479                    (delegator, validator),
480                    |stake| -> AnyResult<_> {
481                        let mut stake = stake.expect("all stakers in validator_info should exist");
482                        stake.stake *= remaining_percentage;
483
484                        Ok(stake)
485                    },
486                )?;
487            }
488        }
489        // go through the queue to slash all pending unbondings
490        let mut unbonding_queue = UNBONDING_QUEUE
491            .may_load(staking_storage)?
492            .unwrap_or_default();
493        unbonding_queue
494            .iter_mut()
495            .filter(|ub| &ub.validator == validator)
496            .for_each(|mut ub| {
497                ub.amount = ub.amount * remaining_percentage;
498            });
499        UNBONDING_QUEUE.save(staking_storage, &unbonding_queue)?;
500
501        VALIDATOR_INFO.save(staking_storage, validator, &validator_info)?;
502        Ok(())
503    }
504
505    // Asserts that the given coin has the proper denominator
506    fn validate_denom(&self, staking_storage: &dyn Storage, amount: &Coin) -> AnyResult<()> {
507        let staking_info = Self::get_staking_info(staking_storage)?;
508        ensure_eq!(
509            amount.denom,
510            staking_info.bonded_denom,
511            anyhow!(
512                "cannot delegate coins of denominator {}, only of {}",
513                amount.denom,
514                staking_info.bonded_denom
515            )
516        );
517        Ok(())
518    }
519
520    // Asserts that the given coin has the proper denominator
521    fn validate_percentage(&self, percentage: Decimal) -> AnyResult<()> {
522        ensure!(percentage <= Decimal::one(), anyhow!("expected percentage"));
523        Ok(())
524    }
525}
526
527impl Staking for StakeKeeper {}
528
529impl Module for StakeKeeper {
530    type ExecT = StakingMsg;
531    type QueryT = StakingQuery;
532    type SudoT = StakingSudo;
533
534    fn execute<ExecC, QueryC: CustomQuery>(
535        &self,
536        api: &dyn Api,
537        storage: &mut dyn Storage,
538        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
539        block: &BlockInfo,
540        sender: Addr,
541        msg: StakingMsg,
542    ) -> AnyResult<AppResponse> {
543        let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
544        match msg {
545            StakingMsg::Delegate { validator, amount } => {
546                let validator = api.addr_validate(&validator)?;
547
548                // see https://github.com/cosmos/cosmos-sdk/blob/v0.46.1/x/staking/keeper/msg_server.go#L251-L256
549                let events = vec![Event::new("delegate")
550                    .add_attribute("validator", &validator)
551                    .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))
552                    .add_attribute("new_shares", amount.amount.to_string())]; // TODO: calculate shares?
553                self.add_stake(
554                    api,
555                    &mut staking_storage,
556                    block,
557                    &sender,
558                    &validator,
559                    amount.clone(),
560                )?;
561                // move money from sender account to this module (note we can control sender here)
562                if !amount.amount.is_zero() {
563                    router.execute(
564                        api,
565                        storage,
566                        block,
567                        sender,
568                        BankMsg::Send {
569                            to_address: self.module_addr.to_string(),
570                            amount: vec![amount],
571                        }
572                        .into(),
573                    )?;
574                }
575                Ok(AppResponse { events, data: None })
576            }
577            StakingMsg::Undelegate { validator, amount } => {
578                let validator = api.addr_validate(&validator)?;
579                self.validate_denom(&staking_storage, &amount)?;
580
581                // see https://github.com/cosmos/cosmos-sdk/blob/v0.46.1/x/staking/keeper/msg_server.go#L378-L383
582                let events = vec![Event::new("unbond")
583                    .add_attribute("validator", &validator)
584                    .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))
585                    .add_attribute("completion_time", "2022-09-27T14:00:00+00:00")]; // TODO: actual date?
586                self.remove_stake(
587                    api,
588                    &mut staking_storage,
589                    block,
590                    &sender,
591                    &validator,
592                    amount.clone(),
593                )?;
594                // add tokens to unbonding queue
595                let staking_info = Self::get_staking_info(&staking_storage)?;
596                let mut unbonding_queue = UNBONDING_QUEUE
597                    .may_load(&staking_storage)?
598                    .unwrap_or_default();
599                unbonding_queue.push_back(Unbonding {
600                    delegator: sender.clone(),
601                    validator,
602                    amount: amount.amount,
603                    payout_at: block.time.plus_seconds(staking_info.unbonding_time),
604                });
605                UNBONDING_QUEUE.save(&mut staking_storage, &unbonding_queue)?;
606                Ok(AppResponse { events, data: None })
607            }
608            StakingMsg::Redelegate {
609                src_validator,
610                dst_validator,
611                amount,
612            } => {
613                let src_validator = api.addr_validate(&src_validator)?;
614                let dst_validator = api.addr_validate(&dst_validator)?;
615                // see https://github.com/cosmos/cosmos-sdk/blob/v0.46.1/x/staking/keeper/msg_server.go#L316-L322
616                let events = vec![Event::new("redelegate")
617                    .add_attribute("source_validator", &src_validator)
618                    .add_attribute("destination_validator", &dst_validator)
619                    .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))];
620
621                self.remove_stake(
622                    api,
623                    &mut staking_storage,
624                    block,
625                    &sender,
626                    &src_validator,
627                    amount.clone(),
628                )?;
629                self.add_stake(
630                    api,
631                    &mut staking_storage,
632                    block,
633                    &sender,
634                    &dst_validator,
635                    amount,
636                )?;
637
638                Ok(AppResponse { events, data: None })
639            }
640            m => bail!("Unsupported staking message: {:?}", m),
641        }
642    }
643
644    fn sudo<ExecC, QueryC: CustomQuery>(
645        &self,
646        api: &dyn Api,
647        storage: &mut dyn Storage,
648        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
649        block: &BlockInfo,
650        msg: StakingSudo,
651    ) -> AnyResult<AppResponse> {
652        match msg {
653            StakingSudo::Slash {
654                validator,
655                percentage,
656            } => {
657                let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
658                let validator = api.addr_validate(&validator)?;
659                self.validate_percentage(percentage)?;
660
661                self.slash(api, &mut staking_storage, block, &validator, percentage)?;
662
663                Ok(AppResponse::default())
664            }
665            StakingSudo::ProcessQueue {} => {
666                let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
667                let mut unbonding_queue = UNBONDING_QUEUE
668                    .may_load(&staking_storage)?
669                    .unwrap_or_default();
670                loop {
671                    let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
672                    match unbonding_queue.front() {
673                        // assuming the queue is sorted by payout_at
674                        Some(Unbonding { payout_at, .. }) if payout_at <= &block.time => {
675                            // remove from queue
676                            let Unbonding {
677                                delegator,
678                                validator,
679                                amount,
680                                ..
681                            } = unbonding_queue.pop_front().unwrap();
682
683                            // remove staking entry if it is empty
684                            let delegation = self
685                                .get_stake(&staking_storage, &delegator, &validator)?
686                                .map(|mut stake| {
687                                    // add unbonding amounts
688                                    stake.amount += unbonding_queue
689                                        .iter()
690                                        .filter(|u| {
691                                            u.delegator == delegator && u.validator == validator
692                                        })
693                                        .map(|u| u.amount)
694                                        .sum::<Uint128>();
695                                    stake
696                                });
697                            match delegation {
698                                Some(delegation) if delegation.amount.is_zero() => {
699                                    STAKES.remove(&mut staking_storage, (&delegator, &validator));
700                                }
701                                None => {
702                                    STAKES.remove(&mut staking_storage, (&delegator, &validator))
703                                }
704                                _ => {}
705                            }
706
707                            let staking_info = Self::get_staking_info(&staking_storage)?;
708                            if !amount.is_zero() {
709                                router.execute(
710                                    api,
711                                    storage,
712                                    block,
713                                    self.module_addr.clone(),
714                                    BankMsg::Send {
715                                        to_address: delegator.into_string(),
716                                        amount: vec![coin(
717                                            amount.u128(),
718                                            &staking_info.bonded_denom,
719                                        )],
720                                    }
721                                    .into(),
722                                )?;
723                            }
724                        }
725                        _ => break,
726                    }
727                }
728                let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
729                UNBONDING_QUEUE.save(&mut staking_storage, &unbonding_queue)?;
730                Ok(AppResponse::default())
731            }
732        }
733    }
734
735    fn query(
736        &self,
737        api: &dyn Api,
738        storage: &dyn Storage,
739        _querier: &dyn Querier,
740        block: &BlockInfo,
741        request: StakingQuery,
742    ) -> AnyResult<Binary> {
743        let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
744        match request {
745            StakingQuery::BondedDenom {} => Ok(to_binary(&BondedDenomResponse {
746                denom: Self::get_staking_info(&staking_storage)?.bonded_denom,
747            })?),
748            StakingQuery::AllDelegations { delegator } => {
749                let delegator = api.addr_validate(&delegator)?;
750                let validators = self.get_validators(&staking_storage)?;
751
752                let mut unbondings = HashMap::new();
753                for ub in UNBONDING_QUEUE
754                    .may_load(&staking_storage)?
755                    .unwrap_or_default()
756                    .into_iter()
757                    .filter(|ub| ub.delegator == delegator)
758                {
759                    *unbondings.entry(ub.validator.into_string()).or_default() += ub.amount;
760                }
761
762                let res: AnyResult<Vec<Delegation>> = validators
763                    .into_iter()
764                    .filter_map(|validator| {
765                        let delegator = delegator.clone();
766                        let amount = self
767                            .get_stake(
768                                &staking_storage,
769                                &delegator,
770                                &Addr::unchecked(&validator.address),
771                            )
772                            .transpose()?;
773
774                        Some(amount.map(|mut amount| {
775                            // include unbonding amounts, mimicing the behaviour of the Cosmos SDK
776                            amount.amount += unbondings
777                                .get(&validator.address)
778                                .copied()
779                                .unwrap_or(Uint128::zero());
780                            Delegation {
781                                delegator,
782                                validator: validator.address,
783                                amount,
784                            }
785                        }))
786                    })
787                    .collect();
788
789                Ok(to_binary(&AllDelegationsResponse { delegations: res? })?)
790            }
791            StakingQuery::Delegation {
792                delegator,
793                validator,
794            } => {
795                let validator_addr = Addr::unchecked(&validator);
796                let validator_obj = match self.get_validator(&staking_storage, &validator_addr)? {
797                    Some(validator) => validator,
798                    None => bail!("non-existent validator {}", validator),
799                };
800                let delegator = api.addr_validate(&delegator)?;
801
802                let shares = STAKES
803                    .may_load(&staking_storage, (&delegator, &validator_addr))?
804                    .unwrap_or_default();
805
806                let validator_info = VALIDATOR_INFO.load(&staking_storage, &validator_addr)?;
807                let reward = Self::get_rewards_internal(
808                    &staking_storage,
809                    block,
810                    &shares,
811                    &validator_obj,
812                    &validator_info,
813                )?;
814                let staking_info = Self::get_staking_info(&staking_storage)?;
815
816                // include unbonding amounts, mimicing the behaviour of the Cosmos SDK
817                let unbonding_amounts: Uint128 = UNBONDING_QUEUE
818                    .may_load(&staking_storage)?
819                    .unwrap_or_default()
820                    .into_iter()
821                    .filter(|ub| ub.delegator == delegator && ub.validator == validator)
822                    .map(|ub| ub.amount)
823                    .sum();
824
825                let amount = coin(
826                    (shares.stake * Uint128::new(1))
827                        .checked_add(unbonding_amounts)?
828                        .u128(),
829                    staking_info.bonded_denom,
830                );
831
832                let full_delegation_response = if amount.amount.is_zero() {
833                    // no delegation
834                    DelegationResponse { delegation: None }
835                } else {
836                    DelegationResponse {
837                        delegation: Some(FullDelegation {
838                            delegator,
839                            validator,
840                            amount: amount.clone(),
841                            can_redelegate: amount, // TODO: not implemented right now
842                            accumulated_rewards: if reward.amount.is_zero() {
843                                vec![]
844                            } else {
845                                vec![reward]
846                            },
847                        }),
848                    }
849                };
850
851                let res = to_binary(&full_delegation_response)?;
852                Ok(res)
853            }
854            StakingQuery::AllValidators {} => Ok(to_binary(&AllValidatorsResponse {
855                validators: self.get_validators(&staking_storage)?,
856            })?),
857            StakingQuery::Validator { address } => Ok(to_binary(&ValidatorResponse {
858                validator: self.get_validator(&staking_storage, &Addr::unchecked(address))?,
859            })?),
860            q => bail!("Unsupported staking sudo message: {:?}", q),
861        }
862    }
863}
864
865#[derive(Default)]
866pub struct DistributionKeeper {}
867
868impl DistributionKeeper {
869    pub fn new() -> Self {
870        DistributionKeeper {}
871    }
872
873    /// Removes all rewards from the given (delegator, validator) pair and returns the amount
874    pub fn remove_rewards(
875        &self,
876        api: &dyn Api,
877        storage: &mut dyn Storage,
878        block: &BlockInfo,
879        delegator: &Addr,
880        validator: &Addr,
881    ) -> AnyResult<Uint128> {
882        let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
883        // update the validator and staker rewards
884        StakeKeeper::update_rewards(api, &mut staking_storage, block, validator)?;
885
886        // load updated rewards for delegator
887        let mut shares = STAKES.load(&staking_storage, (delegator, validator))?;
888        let rewards = Uint128::new(1) * shares.rewards; // convert to Uint128
889
890        // remove rewards from delegator
891        shares.rewards = Decimal::zero();
892        STAKES.save(&mut staking_storage, (delegator, validator), &shares)?;
893
894        Ok(rewards)
895    }
896
897    pub fn get_withdraw_address(storage: &dyn Storage, delegator: &Addr) -> AnyResult<Addr> {
898        Ok(match WITHDRAW_ADDRESS.may_load(storage, delegator)? {
899            Some(a) => a,
900            None => delegator.clone(),
901        })
902    }
903
904    // https://docs.cosmos.network/main/modules/distribution#msgsetwithdrawaddress
905    pub fn set_withdraw_address(
906        storage: &mut dyn Storage,
907        delegator: &Addr,
908        withdraw_address: &Addr,
909    ) -> AnyResult<()> {
910        if delegator == withdraw_address {
911            WITHDRAW_ADDRESS.remove(storage, delegator);
912            Ok(())
913        } else {
914            // technically we should require that this address is not
915            // the address of a module. TODO: how?
916            WITHDRAW_ADDRESS
917                .save(storage, delegator, withdraw_address)
918                .map_err(|e| e.into())
919        }
920    }
921}
922
923impl Distribution for DistributionKeeper {}
924
925impl Module for DistributionKeeper {
926    type ExecT = DistributionMsg;
927    type QueryT = Empty;
928    type SudoT = Empty;
929
930    fn execute<ExecC, QueryC: CustomQuery>(
931        &self,
932        api: &dyn Api,
933        storage: &mut dyn Storage,
934        router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
935        block: &BlockInfo,
936        sender: Addr,
937        msg: DistributionMsg,
938    ) -> AnyResult<AppResponse> {
939        match msg {
940            DistributionMsg::WithdrawDelegatorReward { validator } => {
941                let validator_addr = api.addr_validate(&validator)?;
942
943                let rewards = self.remove_rewards(api, storage, block, &sender, &validator_addr)?;
944
945                let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
946                let distribution_storage = prefixed_read(storage, NAMESPACE_DISTRIBUTION);
947                let staking_info = StakeKeeper::get_staking_info(&staking_storage)?;
948                let receiver = Self::get_withdraw_address(&distribution_storage, &sender)?;
949                // directly mint rewards to delegator
950                router.sudo(
951                    api,
952                    storage,
953                    block,
954                    BankSudo::Mint {
955                        to_address: receiver.into_string(),
956                        amount: vec![Coin {
957                            amount: rewards,
958                            denom: staking_info.bonded_denom.clone(),
959                        }],
960                    }
961                    .into(),
962                )?;
963
964                let events = vec![Event::new("withdraw_delegator_reward")
965                    .add_attribute("validator", &validator)
966                    .add_attribute("sender", &sender)
967                    .add_attribute(
968                        "amount",
969                        format!("{}{}", rewards, staking_info.bonded_denom),
970                    )];
971                Ok(AppResponse { events, data: None })
972            }
973            DistributionMsg::SetWithdrawAddress { address } => {
974                let address = api.addr_validate(&address)?;
975                // https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/keeper/msg_server.go#L38
976                let storage = &mut prefixed(storage, NAMESPACE_DISTRIBUTION);
977                Self::set_withdraw_address(storage, &sender, &address)?;
978                Ok(AppResponse {
979                    data: None,
980                    // https://github.com/cosmos/cosmos-sdk/blob/4f6f6c00021f4b5ee486bbb71ae2071a8ceb47c9/x/distribution/keeper/keeper.go#L74
981                    events: vec![Event::new("set_withdraw_address")
982                        .add_attribute("withdraw_address", address)],
983                })
984            }
985            m => bail!("Unsupported distribution message: {:?}", m),
986        }
987    }
988
989    fn sudo<ExecC, QueryC>(
990        &self,
991        _api: &dyn Api,
992        _storage: &mut dyn Storage,
993        _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
994        _block: &BlockInfo,
995        _msg: Empty,
996    ) -> AnyResult<AppResponse> {
997        bail!("Something went wrong - Distribution doesn't have sudo messages")
998    }
999
1000    fn query(
1001        &self,
1002        _api: &dyn Api,
1003        _storage: &dyn Storage,
1004        _querier: &dyn Querier,
1005        _block: &BlockInfo,
1006        _request: Empty,
1007    ) -> AnyResult<Binary> {
1008        bail!("Something went wrong - Distribution doesn't have query messages")
1009    }
1010}
1011
1012#[cfg(test)]
1013mod test {
1014    use crate::{
1015        app::MockRouter, stargate::StargateKeeper, BankKeeper, FailingModule, Router, WasmKeeper,
1016    };
1017
1018    use super::*;
1019
1020    use cosmwasm_std::{
1021        from_slice,
1022        testing::{mock_env, MockApi, MockStorage},
1023        BalanceResponse, BankQuery, GovMsg, IbcMsg, IbcQuery,
1024    };
1025
1026    /// Type alias for default build `Router` to make its reference in typical scenario
1027    type BasicRouter<ExecC = Empty, QueryC = Empty> = Router<
1028        BankKeeper,
1029        FailingModule<ExecC, QueryC, Empty>,
1030        WasmKeeper<ExecC, QueryC>,
1031        StakeKeeper,
1032        DistributionKeeper,
1033        FailingModule<IbcMsg, IbcQuery, Empty>,
1034        FailingModule<GovMsg, Empty, Empty>,
1035        StargateKeeper<ExecC, QueryC>,
1036    >;
1037
1038    fn mock_router() -> BasicRouter {
1039        Router {
1040            wasm: WasmKeeper::new(),
1041            bank: BankKeeper::new(),
1042            custom: FailingModule::new(),
1043            staking: StakeKeeper::new(),
1044            distribution: DistributionKeeper::new(),
1045            ibc: FailingModule::new(),
1046            gov: FailingModule::new(),
1047            stargate: StargateKeeper::new(),
1048        }
1049    }
1050
1051    fn setup_test_env(
1052        apr: Decimal,
1053        validator_commission: Decimal,
1054    ) -> (MockApi, MockStorage, BasicRouter, BlockInfo, Addr) {
1055        let api = MockApi::default();
1056        let router = mock_router();
1057        let mut store = MockStorage::new();
1058        let block = mock_env().block;
1059
1060        let validator = api.addr_validate("testvaloper1").unwrap();
1061
1062        router
1063            .staking
1064            .setup(
1065                &mut store,
1066                StakingInfo {
1067                    bonded_denom: "TOKEN".to_string(),
1068                    unbonding_time: 60,
1069                    apr,
1070                },
1071            )
1072            .unwrap();
1073
1074        // add validator
1075        let valoper1 = Validator {
1076            address: "testvaloper1".to_string(),
1077            commission: validator_commission,
1078            max_commission: Decimal::percent(100),
1079            max_change_rate: Decimal::percent(1),
1080        };
1081        router
1082            .staking
1083            .add_validator(&api, &mut store, &block, valoper1)
1084            .unwrap();
1085
1086        (api, store, router, block, validator)
1087    }
1088
1089    #[test]
1090    fn add_get_validators() {
1091        let api = MockApi::default();
1092        let mut store = MockStorage::new();
1093        let stake = StakeKeeper::default();
1094        let block = mock_env().block;
1095
1096        // add validator
1097        let valoper1 = Validator {
1098            address: "testvaloper1".to_string(),
1099            commission: Decimal::percent(10),
1100            max_commission: Decimal::percent(20),
1101            max_change_rate: Decimal::percent(1),
1102        };
1103        stake
1104            .add_validator(&api, &mut store, &block, valoper1.clone())
1105            .unwrap();
1106
1107        // get it
1108        let staking_storage = prefixed_read(&store, NAMESPACE_STAKING);
1109        let val = stake
1110            .get_validator(
1111                &staking_storage,
1112                &api.addr_validate("testvaloper1").unwrap(),
1113            )
1114            .unwrap()
1115            .unwrap();
1116        assert_eq!(val, valoper1);
1117
1118        // try to add with same address
1119        let valoper1_fake = Validator {
1120            address: "testvaloper1".to_string(),
1121            commission: Decimal::percent(1),
1122            max_commission: Decimal::percent(10),
1123            max_change_rate: Decimal::percent(100),
1124        };
1125        stake
1126            .add_validator(&api, &mut store, &block, valoper1_fake)
1127            .unwrap_err();
1128
1129        // should still be original value
1130        let staking_storage = prefixed_read(&store, NAMESPACE_STAKING);
1131        let val = stake
1132            .get_validator(
1133                &staking_storage,
1134                &api.addr_validate("testvaloper1").unwrap(),
1135            )
1136            .unwrap()
1137            .unwrap();
1138        assert_eq!(val, valoper1);
1139    }
1140
1141    #[test]
1142    fn validator_slashing() {
1143        let api = MockApi::default();
1144        let router = MockRouter::default();
1145        let mut store = MockStorage::new();
1146        let stake = StakeKeeper::new();
1147        let block = mock_env().block;
1148
1149        let delegator = Addr::unchecked("delegator");
1150        let validator = api.addr_validate("testvaloper1").unwrap();
1151
1152        // add validator
1153        let valoper1 = Validator {
1154            address: "testvaloper1".to_string(),
1155            commission: Decimal::percent(10),
1156            max_commission: Decimal::percent(20),
1157            max_change_rate: Decimal::percent(1),
1158        };
1159        stake
1160            .add_validator(&api, &mut store, &block, valoper1)
1161            .unwrap();
1162
1163        // stake 100 tokens
1164        let mut staking_storage = prefixed(&mut store, NAMESPACE_STAKING);
1165        stake
1166            .add_stake(
1167                &api,
1168                &mut staking_storage,
1169                &block,
1170                &delegator,
1171                &validator,
1172                coin(100, "TOKEN"),
1173            )
1174            .unwrap();
1175
1176        // slash 50%
1177        stake
1178            .sudo(
1179                &api,
1180                &mut store,
1181                &router,
1182                &block,
1183                StakingSudo::Slash {
1184                    validator: "testvaloper1".to_string(),
1185                    percentage: Decimal::percent(50),
1186                },
1187            )
1188            .unwrap();
1189
1190        // check stake
1191        let staking_storage = prefixed(&mut store, NAMESPACE_STAKING);
1192        let stake_left = stake
1193            .get_stake(&staking_storage, &delegator, &validator)
1194            .unwrap();
1195        assert_eq!(
1196            stake_left.unwrap().amount.u128(),
1197            50,
1198            "should have slashed 50%"
1199        );
1200
1201        // slash all
1202        stake
1203            .sudo(
1204                &api,
1205                &mut store,
1206                &router,
1207                &block,
1208                StakingSudo::Slash {
1209                    validator: "testvaloper1".to_string(),
1210                    percentage: Decimal::percent(100),
1211                },
1212            )
1213            .unwrap();
1214
1215        // check stake
1216        let staking_storage = prefixed(&mut store, NAMESPACE_STAKING);
1217        let stake_left = stake
1218            .get_stake(&staking_storage, &delegator, &validator)
1219            .unwrap();
1220        assert_eq!(stake_left, None, "should have slashed whole stake");
1221    }
1222
1223    #[test]
1224    fn rewards_work_for_single_delegator() {
1225        let (api, mut store, router, mut block, validator) =
1226            setup_test_env(Decimal::percent(10), Decimal::percent(10));
1227        let stake = &router.staking;
1228        let distr = &router.distribution;
1229        let delegator = Addr::unchecked("delegator");
1230
1231        let mut staking_storage = prefixed(&mut store, NAMESPACE_STAKING);
1232        // stake 200 tokens
1233        stake
1234            .add_stake(
1235                &api,
1236                &mut staking_storage,
1237                &block,
1238                &delegator,
1239                &validator,
1240                coin(200, "TOKEN"),
1241            )
1242            .unwrap();
1243
1244        // wait 1/2 year
1245        block.time = block.time.plus_seconds(60 * 60 * 24 * 365 / 2);
1246
1247        // should now have 200 * 10% / 2 - 10% commission = 9 tokens reward
1248        let rewards = stake
1249            .get_rewards(&store, &block, &delegator, &validator)
1250            .unwrap()
1251            .unwrap();
1252        assert_eq!(rewards.amount.u128(), 9, "should have 9 tokens reward");
1253
1254        // withdraw rewards
1255        distr
1256            .execute(
1257                &api,
1258                &mut store,
1259                &router,
1260                &block,
1261                delegator.clone(),
1262                DistributionMsg::WithdrawDelegatorReward {
1263                    validator: validator.to_string(),
1264                },
1265            )
1266            .unwrap();
1267
1268        // should have no rewards left
1269        let rewards = stake
1270            .get_rewards(&store, &block, &delegator, &validator)
1271            .unwrap()
1272            .unwrap();
1273        assert_eq!(rewards.amount.u128(), 0);
1274
1275        // wait another 1/2 year
1276        block.time = block.time.plus_seconds(60 * 60 * 24 * 365 / 2);
1277        // should now have 9 tokens again
1278        let rewards = stake
1279            .get_rewards(&store, &block, &delegator, &validator)
1280            .unwrap()
1281            .unwrap();
1282        assert_eq!(rewards.amount.u128(), 9);
1283    }
1284
1285    #[test]
1286    fn rewards_work_for_multiple_delegators() {
1287        let (api, mut store, router, mut block, validator) =
1288            setup_test_env(Decimal::percent(10), Decimal::percent(10));
1289        let stake = &router.staking;
1290        let distr = &router.distribution;
1291        let bank = &router.bank;
1292        let delegator1 = Addr::unchecked("delegator1");
1293        let delegator2 = Addr::unchecked("delegator2");
1294
1295        let mut staking_storage = prefixed(&mut store, NAMESPACE_STAKING);
1296
1297        // add 100 stake to delegator1 and 200 to delegator2
1298        stake
1299            .add_stake(
1300                &api,
1301                &mut staking_storage,
1302                &block,
1303                &delegator1,
1304                &validator,
1305                coin(100, "TOKEN"),
1306            )
1307            .unwrap();
1308        stake
1309            .add_stake(
1310                &api,
1311                &mut staking_storage,
1312                &block,
1313                &delegator2,
1314                &validator,
1315                coin(200, "TOKEN"),
1316            )
1317            .unwrap();
1318
1319        // wait 1 year
1320        block.time = block.time.plus_seconds(60 * 60 * 24 * 365);
1321
1322        // delegator1 should now have 100 * 10% - 10% commission = 9 tokens
1323        let rewards = stake
1324            .get_rewards(&store, &block, &delegator1, &validator)
1325            .unwrap()
1326            .unwrap();
1327        assert_eq!(rewards.amount.u128(), 9);
1328
1329        // delegator2 should now have 200 * 10% - 10% commission = 18 tokens
1330        let rewards = stake
1331            .get_rewards(&store, &block, &delegator2, &validator)
1332            .unwrap()
1333            .unwrap();
1334        assert_eq!(rewards.amount.u128(), 18);
1335
1336        // delegator1 stakes 100 more
1337        let mut staking_storage = prefixed(&mut store, NAMESPACE_STAKING);
1338        stake
1339            .add_stake(
1340                &api,
1341                &mut staking_storage,
1342                &block,
1343                &delegator1,
1344                &validator,
1345                coin(100, "TOKEN"),
1346            )
1347            .unwrap();
1348
1349        // wait another year
1350        block.time = block.time.plus_seconds(60 * 60 * 24 * 365);
1351
1352        // delegator1 should now have 9 + 200 * 10% - 10% commission = 27 tokens
1353        let rewards = stake
1354            .get_rewards(&store, &block, &delegator1, &validator)
1355            .unwrap()
1356            .unwrap();
1357        assert_eq!(rewards.amount.u128(), 27);
1358
1359        // delegator2 should now have 18 + 200 * 10% - 10% commission = 36 tokens
1360        let rewards = stake
1361            .get_rewards(&store, &block, &delegator2, &validator)
1362            .unwrap()
1363            .unwrap();
1364        assert_eq!(rewards.amount.u128(), 36);
1365
1366        // delegator2 unstakes 100 (has 100 left after that)
1367        let mut staking_storage = prefixed(&mut store, NAMESPACE_STAKING);
1368        stake
1369            .remove_stake(
1370                &api,
1371                &mut staking_storage,
1372                &block,
1373                &delegator2,
1374                &validator,
1375                coin(100, "TOKEN"),
1376            )
1377            .unwrap();
1378
1379        // and delegator1 withdraws rewards
1380        distr
1381            .execute(
1382                &api,
1383                &mut store,
1384                &router,
1385                &block,
1386                delegator1.clone(),
1387                DistributionMsg::WithdrawDelegatorReward {
1388                    validator: validator.to_string(),
1389                },
1390            )
1391            .unwrap();
1392
1393        let balance: BalanceResponse = from_slice(
1394            &bank
1395                .query(
1396                    &api,
1397                    &store,
1398                    &router.querier(&api, &store, &block),
1399                    &block,
1400                    BankQuery::Balance {
1401                        address: delegator1.to_string(),
1402                        denom: "TOKEN".to_string(),
1403                    },
1404                )
1405                .unwrap(),
1406        )
1407        .unwrap();
1408        assert_eq!(
1409            balance.amount.amount.u128(),
1410            27,
1411            "withdraw should change bank balance"
1412        );
1413        let rewards = stake
1414            .get_rewards(&store, &block, &delegator1, &validator)
1415            .unwrap()
1416            .unwrap();
1417        assert_eq!(
1418            rewards.amount.u128(),
1419            0,
1420            "withdraw should reduce rewards to 0"
1421        );
1422
1423        // wait another year
1424        block.time = block.time.plus_seconds(60 * 60 * 24 * 365);
1425
1426        // delegator1 should now have 0 + 200 * 10% - 10% commission = 18 tokens
1427        let rewards = stake
1428            .get_rewards(&store, &block, &delegator1, &validator)
1429            .unwrap()
1430            .unwrap();
1431        assert_eq!(rewards.amount.u128(), 18);
1432
1433        // delegator2 should now have 36 + 100 * 10% - 10% commission = 45 tokens
1434        let rewards = stake
1435            .get_rewards(&store, &block, &delegator2, &validator)
1436            .unwrap()
1437            .unwrap();
1438        assert_eq!(rewards.amount.u128(), 45);
1439    }
1440
1441    mod msg {
1442        use cosmwasm_std::{coins, from_slice, Addr, BondedDenomResponse, Decimal, StakingQuery};
1443        use serde::de::DeserializeOwned;
1444
1445        use super::*;
1446
1447        // shortens tests a bit
1448        struct TestEnv {
1449            api: MockApi,
1450            store: MockStorage,
1451            router: BasicRouter,
1452            block: BlockInfo,
1453        }
1454
1455        impl TestEnv {
1456            fn wrap(tuple: (MockApi, MockStorage, BasicRouter, BlockInfo, Addr)) -> (Self, Addr) {
1457                (
1458                    Self {
1459                        api: tuple.0,
1460                        store: tuple.1,
1461                        router: tuple.2,
1462                        block: tuple.3,
1463                    },
1464                    tuple.4,
1465                )
1466            }
1467        }
1468
1469        fn execute_stake(
1470            env: &mut TestEnv,
1471            sender: Addr,
1472            msg: StakingMsg,
1473        ) -> AnyResult<AppResponse> {
1474            env.router.staking.execute(
1475                &env.api,
1476                &mut env.store,
1477                &env.router,
1478                &env.block,
1479                sender,
1480                msg,
1481            )
1482        }
1483
1484        fn query_stake<T: DeserializeOwned>(env: &TestEnv, msg: StakingQuery) -> AnyResult<T> {
1485            Ok(from_slice(&env.router.staking.query(
1486                &env.api,
1487                &env.store,
1488                &env.router.querier(&env.api, &env.store, &env.block),
1489                &env.block,
1490                msg,
1491            )?)?)
1492        }
1493
1494        fn execute_distr(
1495            env: &mut TestEnv,
1496            sender: Addr,
1497            msg: DistributionMsg,
1498        ) -> AnyResult<AppResponse> {
1499            env.router.distribution.execute(
1500                &env.api,
1501                &mut env.store,
1502                &env.router,
1503                &env.block,
1504                sender,
1505                msg,
1506            )
1507        }
1508
1509        fn query_bank<T: DeserializeOwned>(env: &TestEnv, msg: BankQuery) -> AnyResult<T> {
1510            Ok(from_slice(&env.router.bank.query(
1511                &env.api,
1512                &env.store,
1513                &env.router.querier(&env.api, &env.store, &env.block),
1514                &env.block,
1515                msg,
1516            )?)?)
1517        }
1518
1519        fn assert_balances(env: &TestEnv, balances: impl IntoIterator<Item = (Addr, u128)>) {
1520            for (addr, amount) in balances {
1521                let balance: BalanceResponse = query_bank(
1522                    env,
1523                    BankQuery::Balance {
1524                        address: addr.to_string(),
1525                        denom: "TOKEN".to_string(),
1526                    },
1527                )
1528                .unwrap();
1529                assert_eq!(balance.amount.amount.u128(), amount);
1530            }
1531        }
1532
1533        #[test]
1534        fn execute() {
1535            // test all execute msgs
1536            let (mut test_env, validator1) =
1537                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
1538
1539            let delegator1 = Addr::unchecked("delegator1");
1540            let reward_receiver = Addr::unchecked("rewardreceiver");
1541
1542            // fund delegator1 account
1543            test_env
1544                .router
1545                .bank
1546                .init_balance(&mut test_env.store, &delegator1, vec![coin(1000, "TOKEN")])
1547                .unwrap();
1548
1549            // add second validator
1550            let validator2 = Addr::unchecked("validator2");
1551            test_env
1552                .router
1553                .staking
1554                .add_validator(
1555                    &test_env.api,
1556                    &mut test_env.store,
1557                    &test_env.block,
1558                    Validator {
1559                        address: validator2.to_string(),
1560                        commission: Decimal::zero(),
1561                        max_commission: Decimal::percent(20),
1562                        max_change_rate: Decimal::percent(1),
1563                    },
1564                )
1565                .unwrap();
1566
1567            // delegate 100 tokens to validator1
1568            execute_stake(
1569                &mut test_env,
1570                delegator1.clone(),
1571                StakingMsg::Delegate {
1572                    validator: validator1.to_string(),
1573                    amount: coin(100, "TOKEN"),
1574                },
1575            )
1576            .unwrap();
1577
1578            // should now have 100 tokens less
1579            assert_balances(&test_env, vec![(delegator1.clone(), 900)]);
1580
1581            // wait a year
1582            test_env.block.time = test_env.block.time.plus_seconds(60 * 60 * 24 * 365);
1583
1584            // change the withdrawal address
1585            execute_distr(
1586                &mut test_env,
1587                delegator1.clone(),
1588                DistributionMsg::SetWithdrawAddress {
1589                    address: reward_receiver.to_string(),
1590                },
1591            )
1592            .unwrap();
1593
1594            // withdraw rewards
1595            execute_distr(
1596                &mut test_env,
1597                delegator1.clone(),
1598                DistributionMsg::WithdrawDelegatorReward {
1599                    validator: validator1.to_string(),
1600                },
1601            )
1602            .unwrap();
1603
1604            // withdrawal address received rewards.
1605            assert_balances(
1606                &test_env,
1607                // one year, 10%apr, 10%commision, 100 tokens staked
1608                vec![(reward_receiver, 100 / 10 * 9 / 10)],
1609            );
1610
1611            // redelegate to validator2
1612            execute_stake(
1613                &mut test_env,
1614                delegator1.clone(),
1615                StakingMsg::Redelegate {
1616                    src_validator: validator1.to_string(),
1617                    dst_validator: validator2.to_string(),
1618                    amount: coin(100, "TOKEN"),
1619                },
1620            )
1621            .unwrap();
1622
1623            // should have same amount as before (rewards receiver received rewards).
1624            assert_balances(&test_env, vec![(delegator1.clone(), 900)]);
1625
1626            let delegations: AllDelegationsResponse = query_stake(
1627                &test_env,
1628                StakingQuery::AllDelegations {
1629                    delegator: delegator1.to_string(),
1630                },
1631            )
1632            .unwrap();
1633            assert_eq!(
1634                delegations.delegations,
1635                [Delegation {
1636                    delegator: delegator1.clone(),
1637                    validator: validator2.to_string(),
1638                    amount: coin(100, "TOKEN"),
1639                }]
1640            );
1641
1642            // undelegate all tokens
1643            execute_stake(
1644                &mut test_env,
1645                delegator1.clone(),
1646                StakingMsg::Undelegate {
1647                    validator: validator2.to_string(),
1648                    amount: coin(100, "TOKEN"),
1649                },
1650            )
1651            .unwrap();
1652
1653            // wait for unbonding period (60 seconds in default config)
1654            test_env.block.time = test_env.block.time.plus_seconds(60);
1655
1656            // need to manually cause queue to get processed
1657            test_env
1658                .router
1659                .staking
1660                .sudo(
1661                    &test_env.api,
1662                    &mut test_env.store,
1663                    &test_env.router,
1664                    &test_env.block,
1665                    StakingSudo::ProcessQueue {},
1666                )
1667                .unwrap();
1668
1669            // check bank balance
1670            assert_balances(&test_env, vec![(delegator1.clone(), 1000)]);
1671        }
1672
1673        #[test]
1674        fn can_set_withdraw_address() {
1675            let (mut test_env, validator) =
1676                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
1677
1678            let delegator = Addr::unchecked("delegator");
1679            let reward_receiver = Addr::unchecked("rewardreceiver");
1680
1681            test_env
1682                .router
1683                .bank
1684                .init_balance(&mut test_env.store, &delegator, coins(100, "TOKEN"))
1685                .unwrap();
1686
1687            // Stake 100 tokens to the validator.
1688            execute_stake(
1689                &mut test_env,
1690                delegator.clone(),
1691                StakingMsg::Delegate {
1692                    validator: validator.to_string(),
1693                    amount: coin(100, "TOKEN"),
1694                },
1695            )
1696            .unwrap();
1697
1698            // Change rewards receiver.
1699            execute_distr(
1700                &mut test_env,
1701                delegator.clone(),
1702                DistributionMsg::SetWithdrawAddress {
1703                    address: reward_receiver.to_string(),
1704                },
1705            )
1706            .unwrap();
1707
1708            // A year passes.
1709            test_env.block.time = test_env.block.time.plus_seconds(60 * 60 * 24 * 365);
1710
1711            // Withdraw rewards to reward receiver.
1712            execute_distr(
1713                &mut test_env,
1714                delegator.clone(),
1715                DistributionMsg::WithdrawDelegatorReward {
1716                    validator: validator.to_string(),
1717                },
1718            )
1719            .unwrap();
1720
1721            // Change reward receiver back to delegator.
1722            execute_distr(
1723                &mut test_env,
1724                delegator.clone(),
1725                DistributionMsg::SetWithdrawAddress {
1726                    address: delegator.to_string(),
1727                },
1728            )
1729            .unwrap();
1730
1731            // Another year passes.
1732            test_env.block.time = test_env.block.time.plus_seconds(60 * 60 * 24 * 365);
1733
1734            // Withdraw rewards to delegator.
1735            execute_distr(
1736                &mut test_env,
1737                delegator.clone(),
1738                DistributionMsg::WithdrawDelegatorReward {
1739                    validator: validator.to_string(),
1740                },
1741            )
1742            .unwrap();
1743
1744            // one year, 10%apr, 10%commision, 100 tokens staked
1745            let rewards_yr = 100 / 10 * 9 / 10;
1746
1747            assert_balances(
1748                &test_env,
1749                vec![(reward_receiver, rewards_yr), (delegator, rewards_yr)],
1750            );
1751        }
1752
1753        #[test]
1754        fn cannot_steal() {
1755            let (mut test_env, validator1) =
1756                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
1757
1758            let delegator1 = Addr::unchecked("delegator1");
1759
1760            // fund delegator1 account
1761            test_env
1762                .router
1763                .bank
1764                .init_balance(&mut test_env.store, &delegator1, vec![coin(100, "TOKEN")])
1765                .unwrap();
1766
1767            // delegate 100 tokens to validator1
1768            execute_stake(
1769                &mut test_env,
1770                delegator1.clone(),
1771                StakingMsg::Delegate {
1772                    validator: validator1.to_string(),
1773                    amount: coin(100, "TOKEN"),
1774                },
1775            )
1776            .unwrap();
1777
1778            // undelegate more tokens than we have
1779            let e = execute_stake(
1780                &mut test_env,
1781                delegator1.clone(),
1782                StakingMsg::Undelegate {
1783                    validator: validator1.to_string(),
1784                    amount: coin(200, "TOKEN"),
1785                },
1786            )
1787            .unwrap_err();
1788
1789            assert_eq!(e.to_string(), "insufficient stake");
1790
1791            // add second validator
1792            let validator2 = Addr::unchecked("validator2");
1793            test_env
1794                .router
1795                .staking
1796                .add_validator(
1797                    &test_env.api,
1798                    &mut test_env.store,
1799                    &test_env.block,
1800                    Validator {
1801                        address: validator2.to_string(),
1802                        commission: Decimal::zero(),
1803                        max_commission: Decimal::percent(20),
1804                        max_change_rate: Decimal::percent(1),
1805                    },
1806                )
1807                .unwrap();
1808
1809            // redelegate more tokens than we have
1810            let e = execute_stake(
1811                &mut test_env,
1812                delegator1.clone(),
1813                StakingMsg::Redelegate {
1814                    src_validator: validator1.to_string(),
1815                    dst_validator: validator2.to_string(),
1816                    amount: coin(200, "TOKEN"),
1817                },
1818            )
1819            .unwrap_err();
1820            assert_eq!(e.to_string(), "insufficient stake");
1821        }
1822
1823        #[test]
1824        fn denom_validation() {
1825            let (mut test_env, validator) =
1826                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
1827
1828            let delegator1 = Addr::unchecked("delegator1");
1829
1830            // fund delegator1 account
1831            test_env
1832                .router
1833                .bank
1834                .init_balance(&mut test_env.store, &delegator1, vec![coin(100, "FAKE")])
1835                .unwrap();
1836
1837            // try to delegate 100 to validator1
1838            let e = execute_stake(
1839                &mut test_env,
1840                delegator1.clone(),
1841                StakingMsg::Delegate {
1842                    validator: validator.to_string(),
1843                    amount: coin(100, "FAKE"),
1844                },
1845            )
1846            .unwrap_err();
1847
1848            assert_eq!(
1849                e.to_string(),
1850                "cannot delegate coins of denominator FAKE, only of TOKEN",
1851            );
1852        }
1853
1854        #[test]
1855        fn cannot_slash_nonexistent() {
1856            let (mut test_env, _) =
1857                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
1858
1859            let delegator1 = Addr::unchecked("delegator1");
1860
1861            // fund delegator1 account
1862            test_env
1863                .router
1864                .bank
1865                .init_balance(&mut test_env.store, &delegator1, vec![coin(100, "FAKE")])
1866                .unwrap();
1867
1868            // try to delegate 100 to validator1
1869            let e = test_env
1870                .router
1871                .staking
1872                .sudo(
1873                    &test_env.api,
1874                    &mut test_env.store,
1875                    &test_env.router,
1876                    &test_env.block,
1877                    StakingSudo::Slash {
1878                        validator: "nonexistingvaloper".to_string(),
1879                        percentage: Decimal::percent(50),
1880                    },
1881                )
1882                .unwrap_err();
1883            assert_eq!(e.to_string(), "validator nonexistingvaloper not found");
1884        }
1885
1886        #[test]
1887        fn zero_staking_allowed() {
1888            let (mut test_env, validator) =
1889                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
1890
1891            let delegator = Addr::unchecked("delegator1");
1892
1893            // delegate 0
1894            execute_stake(
1895                &mut test_env,
1896                delegator.clone(),
1897                StakingMsg::Delegate {
1898                    validator: validator.to_string(),
1899                    amount: coin(0, "TOKEN"),
1900                },
1901            )
1902            .unwrap();
1903
1904            // undelegate 0
1905            execute_stake(
1906                &mut test_env,
1907                delegator,
1908                StakingMsg::Undelegate {
1909                    validator: validator.to_string(),
1910                    amount: coin(0, "TOKEN"),
1911                },
1912            )
1913            .unwrap();
1914        }
1915
1916        #[test]
1917        fn query_staking() {
1918            // run all staking queries
1919            let (mut test_env, validator1) =
1920                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
1921            let delegator1 = Addr::unchecked("delegator1");
1922            let delegator2 = Addr::unchecked("delegator2");
1923
1924            // init balances
1925            test_env
1926                .router
1927                .bank
1928                .init_balance(&mut test_env.store, &delegator1, vec![coin(260, "TOKEN")])
1929                .unwrap();
1930            test_env
1931                .router
1932                .bank
1933                .init_balance(&mut test_env.store, &delegator2, vec![coin(150, "TOKEN")])
1934                .unwrap();
1935
1936            // add another validator
1937            let validator2 = test_env.api.addr_validate("testvaloper2").unwrap();
1938            let valoper2 = Validator {
1939                address: "testvaloper2".to_string(),
1940                commission: Decimal::percent(0),
1941                max_commission: Decimal::percent(1),
1942                max_change_rate: Decimal::percent(1),
1943            };
1944            test_env
1945                .router
1946                .staking
1947                .add_validator(
1948                    &test_env.api,
1949                    &mut test_env.store,
1950                    &test_env.block,
1951                    valoper2.clone(),
1952                )
1953                .unwrap();
1954
1955            // query validators
1956            let valoper1: ValidatorResponse = query_stake(
1957                &test_env,
1958                StakingQuery::Validator {
1959                    address: validator1.to_string(),
1960                },
1961            )
1962            .unwrap();
1963            let validators: AllValidatorsResponse =
1964                query_stake(&test_env, StakingQuery::AllValidators {}).unwrap();
1965            assert_eq!(
1966                validators.validators,
1967                [valoper1.validator.unwrap(), valoper2]
1968            );
1969            // query non-existent validator
1970            let response = query_stake::<ValidatorResponse>(
1971                &test_env,
1972                StakingQuery::Validator {
1973                    address: "notvaloper".to_string(),
1974                },
1975            )
1976            .unwrap();
1977            assert_eq!(response.validator, None);
1978
1979            // query bonded denom
1980            let response: BondedDenomResponse =
1981                query_stake(&test_env, StakingQuery::BondedDenom {}).unwrap();
1982            assert_eq!(response.denom, "TOKEN");
1983
1984            // delegate some tokens with delegator1 and delegator2
1985            execute_stake(
1986                &mut test_env,
1987                delegator1.clone(),
1988                StakingMsg::Delegate {
1989                    validator: validator1.to_string(),
1990                    amount: coin(100, "TOKEN"),
1991                },
1992            )
1993            .unwrap();
1994            execute_stake(
1995                &mut test_env,
1996                delegator1.clone(),
1997                StakingMsg::Delegate {
1998                    validator: validator2.to_string(),
1999                    amount: coin(160, "TOKEN"),
2000                },
2001            )
2002            .unwrap();
2003            execute_stake(
2004                &mut test_env,
2005                delegator2.clone(),
2006                StakingMsg::Delegate {
2007                    validator: validator1.to_string(),
2008                    amount: coin(150, "TOKEN"),
2009                },
2010            )
2011            .unwrap();
2012            // unstake some again
2013            execute_stake(
2014                &mut test_env,
2015                delegator1.clone(),
2016                StakingMsg::Undelegate {
2017                    validator: validator1.to_string(),
2018                    amount: coin(50, "TOKEN"),
2019                },
2020            )
2021            .unwrap();
2022            execute_stake(
2023                &mut test_env,
2024                delegator2.clone(),
2025                StakingMsg::Undelegate {
2026                    validator: validator1.to_string(),
2027                    amount: coin(50, "TOKEN"),
2028                },
2029            )
2030            .unwrap();
2031
2032            // query all delegations
2033            let response1: AllDelegationsResponse = query_stake(
2034                &test_env,
2035                StakingQuery::AllDelegations {
2036                    delegator: delegator1.to_string(),
2037                },
2038            )
2039            .unwrap();
2040            assert_eq!(
2041                response1.delegations,
2042                vec![
2043                    Delegation {
2044                        delegator: delegator1.clone(),
2045                        validator: validator1.to_string(),
2046                        amount: coin(100, "TOKEN"),
2047                    },
2048                    Delegation {
2049                        delegator: delegator1.clone(),
2050                        validator: validator2.to_string(),
2051                        amount: coin(160, "TOKEN"),
2052                    },
2053                ]
2054            );
2055            let response2: DelegationResponse = query_stake(
2056                &test_env,
2057                StakingQuery::Delegation {
2058                    delegator: delegator2.to_string(),
2059                    validator: validator1.to_string(),
2060                },
2061            )
2062            .unwrap();
2063            assert_eq!(
2064                response2.delegation.unwrap(),
2065                FullDelegation {
2066                    delegator: delegator2.clone(),
2067                    validator: validator1.to_string(),
2068                    amount: coin(150, "TOKEN"),
2069                    accumulated_rewards: vec![],
2070                    can_redelegate: coin(150, "TOKEN"),
2071                },
2072            );
2073        }
2074
2075        #[test]
2076        fn delegation_queries_unbonding() {
2077            // run all staking queries
2078            let (mut test_env, validator) =
2079                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
2080            let delegator1 = Addr::unchecked("delegator1");
2081            let delegator2 = Addr::unchecked("delegator2");
2082
2083            // init balances
2084            test_env
2085                .router
2086                .bank
2087                .init_balance(&mut test_env.store, &delegator1, vec![coin(100, "TOKEN")])
2088                .unwrap();
2089            test_env
2090                .router
2091                .bank
2092                .init_balance(&mut test_env.store, &delegator2, vec![coin(150, "TOKEN")])
2093                .unwrap();
2094
2095            // delegate some tokens with delegator1 and delegator2
2096            execute_stake(
2097                &mut test_env,
2098                delegator1.clone(),
2099                StakingMsg::Delegate {
2100                    validator: validator.to_string(),
2101                    amount: coin(100, "TOKEN"),
2102                },
2103            )
2104            .unwrap();
2105            execute_stake(
2106                &mut test_env,
2107                delegator2.clone(),
2108                StakingMsg::Delegate {
2109                    validator: validator.to_string(),
2110                    amount: coin(150, "TOKEN"),
2111                },
2112            )
2113            .unwrap();
2114            // unstake some of delegator1's stake
2115            execute_stake(
2116                &mut test_env,
2117                delegator1.clone(),
2118                StakingMsg::Undelegate {
2119                    validator: validator.to_string(),
2120                    amount: coin(50, "TOKEN"),
2121                },
2122            )
2123            .unwrap();
2124            // unstake all of delegator2's stake
2125            execute_stake(
2126                &mut test_env,
2127                delegator2.clone(),
2128                StakingMsg::Undelegate {
2129                    validator: validator.to_string(),
2130                    amount: coin(150, "TOKEN"),
2131                },
2132            )
2133            .unwrap();
2134
2135            // query all delegations
2136            let response1: AllDelegationsResponse = query_stake(
2137                &test_env,
2138                StakingQuery::AllDelegations {
2139                    delegator: delegator1.to_string(),
2140                },
2141            )
2142            .unwrap();
2143            assert_eq!(
2144                response1.delegations,
2145                vec![Delegation {
2146                    delegator: delegator1.clone(),
2147                    validator: validator.to_string(),
2148                    amount: coin(100, "TOKEN"),
2149                }]
2150            );
2151            let response2: DelegationResponse = query_stake(
2152                &test_env,
2153                StakingQuery::Delegation {
2154                    delegator: delegator2.to_string(),
2155                    validator: validator.to_string(),
2156                },
2157            )
2158            .unwrap();
2159            assert_eq!(
2160                response2.delegation.unwrap(),
2161                FullDelegation {
2162                    delegator: delegator2.clone(),
2163                    validator: validator.to_string(),
2164                    amount: coin(150, "TOKEN"),
2165                    accumulated_rewards: vec![],
2166                    can_redelegate: coin(150, "TOKEN"),
2167                },
2168            );
2169
2170            // wait until unbonding time is over
2171            test_env.block.time = test_env.block.time.plus_seconds(60);
2172            test_env
2173                .router
2174                .staking
2175                .sudo(
2176                    &test_env.api,
2177                    &mut test_env.store,
2178                    &test_env.router,
2179                    &test_env.block,
2180                    StakingSudo::ProcessQueue {},
2181                )
2182                .unwrap();
2183
2184            // query all delegations again
2185            let response1: AllDelegationsResponse = query_stake(
2186                &test_env,
2187                StakingQuery::AllDelegations {
2188                    delegator: delegator1.to_string(),
2189                },
2190            )
2191            .unwrap();
2192            assert_eq!(
2193                response1.delegations,
2194                vec![Delegation {
2195                    delegator: delegator1.clone(),
2196                    validator: validator.to_string(),
2197                    amount: coin(50, "TOKEN"),
2198                }],
2199                "delegator1 should have less now"
2200            );
2201            let response2: DelegationResponse = query_stake(
2202                &test_env,
2203                StakingQuery::Delegation {
2204                    delegator: delegator2.to_string(),
2205                    validator: validator.to_string(),
2206                },
2207            )
2208            .unwrap();
2209            assert_eq!(
2210                response2.delegation, None,
2211                "delegator2 should have nothing left"
2212            );
2213
2214            // unstake rest of delegator1's stake in two steps
2215            execute_stake(
2216                &mut test_env,
2217                delegator1.clone(),
2218                StakingMsg::Undelegate {
2219                    validator: validator.to_string(),
2220                    amount: coin(25, "TOKEN"),
2221                },
2222            )
2223            .unwrap();
2224            test_env.block.time = test_env.block.time.plus_seconds(10);
2225            execute_stake(
2226                &mut test_env,
2227                delegator1.clone(),
2228                StakingMsg::Undelegate {
2229                    validator: validator.to_string(),
2230                    amount: coin(25, "TOKEN"),
2231                },
2232            )
2233            .unwrap();
2234
2235            // query all delegations again
2236            let response1: DelegationResponse = query_stake(
2237                &test_env,
2238                StakingQuery::Delegation {
2239                    delegator: delegator1.to_string(),
2240                    validator: validator.to_string(),
2241                },
2242            )
2243            .unwrap();
2244            let response2: AllDelegationsResponse = query_stake(
2245                &test_env,
2246                StakingQuery::AllDelegations {
2247                    delegator: delegator1.to_string(),
2248                },
2249            )
2250            .unwrap();
2251            assert_eq!(
2252                response1.delegation.unwrap().amount.amount.u128(),
2253                50,
2254                "delegator1 should still have 50 tokens unbonding"
2255            );
2256            assert_eq!(response2.delegations[0].amount.amount.u128(), 50);
2257
2258            // wait until unbonding time is over
2259            test_env.block.time = test_env.block.time.plus_seconds(60);
2260            test_env
2261                .router
2262                .staking
2263                .sudo(
2264                    &test_env.api,
2265                    &mut test_env.store,
2266                    &test_env.router,
2267                    &test_env.block,
2268                    StakingSudo::ProcessQueue {},
2269                )
2270                .unwrap();
2271
2272            // query all delegations again
2273            let response1: DelegationResponse = query_stake(
2274                &test_env,
2275                StakingQuery::Delegation {
2276                    delegator: delegator1.to_string(),
2277                    validator: validator.to_string(),
2278                },
2279            )
2280            .unwrap();
2281            let response2: AllDelegationsResponse = query_stake(
2282                &test_env,
2283                StakingQuery::AllDelegations {
2284                    delegator: delegator1.to_string(),
2285                },
2286            )
2287            .unwrap();
2288            assert_eq!(
2289                response1.delegation, None,
2290                "delegator1 should have nothing left"
2291            );
2292            assert!(response2.delegations.is_empty());
2293        }
2294
2295        #[test]
2296        fn partial_unbonding_keeps_stake() {
2297            let (mut test_env, validator) =
2298                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
2299            let delegator = Addr::unchecked("delegator1");
2300
2301            // init balance
2302            test_env
2303                .router
2304                .bank
2305                .init_balance(&mut test_env.store, &delegator, vec![coin(100, "TOKEN")])
2306                .unwrap();
2307
2308            // delegate all tokens
2309            execute_stake(
2310                &mut test_env,
2311                delegator.clone(),
2312                StakingMsg::Delegate {
2313                    validator: validator.to_string(),
2314                    amount: coin(100, "TOKEN"),
2315                },
2316            )
2317            .unwrap();
2318            // unstake in multiple steps
2319            execute_stake(
2320                &mut test_env,
2321                delegator.clone(),
2322                StakingMsg::Undelegate {
2323                    validator: validator.to_string(),
2324                    amount: coin(50, "TOKEN"),
2325                },
2326            )
2327            .unwrap();
2328            test_env.block.time = test_env.block.time.plus_seconds(10);
2329            execute_stake(
2330                &mut test_env,
2331                delegator.clone(),
2332                StakingMsg::Undelegate {
2333                    validator: validator.to_string(),
2334                    amount: coin(30, "TOKEN"),
2335                },
2336            )
2337            .unwrap();
2338            test_env.block.time = test_env.block.time.plus_seconds(10);
2339            execute_stake(
2340                &mut test_env,
2341                delegator.clone(),
2342                StakingMsg::Undelegate {
2343                    validator: validator.to_string(),
2344                    amount: coin(20, "TOKEN"),
2345                },
2346            )
2347            .unwrap();
2348
2349            // wait for first unbonding to complete (but not the others) and process queue
2350            test_env.block.time = test_env.block.time.plus_seconds(40);
2351            test_env
2352                .router
2353                .staking
2354                .sudo(
2355                    &test_env.api,
2356                    &mut test_env.store,
2357                    &test_env.router,
2358                    &test_env.block,
2359                    StakingSudo::ProcessQueue {},
2360                )
2361                .unwrap();
2362
2363            // query delegations
2364            // we now have 0 stake, 50 unbonding and 50 completed unbonding
2365            let response1: DelegationResponse = query_stake(
2366                &test_env,
2367                StakingQuery::Delegation {
2368                    delegator: delegator.to_string(),
2369                    validator: validator.to_string(),
2370                },
2371            )
2372            .unwrap();
2373            let response2: AllDelegationsResponse = query_stake(
2374                &test_env,
2375                StakingQuery::AllDelegations {
2376                    delegator: delegator.to_string(),
2377                },
2378            )
2379            .unwrap();
2380            assert_eq!(
2381                response1.delegation,
2382                Some(FullDelegation {
2383                    delegator: delegator.clone(),
2384                    validator: validator.to_string(),
2385                    amount: coin(50, "TOKEN"),
2386                    can_redelegate: coin(50, "TOKEN"),
2387                    accumulated_rewards: vec![],
2388                })
2389            );
2390            assert_eq!(
2391                response2.delegations,
2392                vec![Delegation {
2393                    delegator: delegator.clone(),
2394                    validator: validator.to_string(),
2395                    amount: coin(50, "TOKEN"),
2396                }]
2397            );
2398
2399            // wait for the rest to complete
2400            test_env.block.time = test_env.block.time.plus_seconds(20);
2401            test_env
2402                .router
2403                .staking
2404                .sudo(
2405                    &test_env.api,
2406                    &mut test_env.store,
2407                    &test_env.router,
2408                    &test_env.block,
2409                    StakingSudo::ProcessQueue {},
2410                )
2411                .unwrap();
2412
2413            // query delegations again
2414            let response1: DelegationResponse = query_stake(
2415                &test_env,
2416                StakingQuery::Delegation {
2417                    delegator: delegator.to_string(),
2418                    validator: validator.to_string(),
2419                },
2420            )
2421            .unwrap();
2422            let response2: AllDelegationsResponse = query_stake(
2423                &test_env,
2424                StakingQuery::AllDelegations {
2425                    delegator: delegator.to_string(),
2426                },
2427            )
2428            .unwrap();
2429            assert_eq!(
2430                response1.delegation, None,
2431                "delegator should have nothing left"
2432            );
2433            assert!(response2.delegations.is_empty());
2434        }
2435
2436        #[test]
2437        fn delegation_queries_slashed() {
2438            // run all staking queries
2439            let (mut test_env, validator) =
2440                TestEnv::wrap(setup_test_env(Decimal::percent(10), Decimal::percent(10)));
2441            let delegator = Addr::unchecked("delegator");
2442
2443            // init balance
2444            test_env
2445                .router
2446                .bank
2447                .init_balance(&mut test_env.store, &delegator, vec![coin(333, "TOKEN")])
2448                .unwrap();
2449
2450            // delegate some tokens
2451            execute_stake(
2452                &mut test_env,
2453                delegator.clone(),
2454                StakingMsg::Delegate {
2455                    validator: validator.to_string(),
2456                    amount: coin(333, "TOKEN"),
2457                },
2458            )
2459            .unwrap();
2460            // unstake some
2461            execute_stake(
2462                &mut test_env,
2463                delegator.clone(),
2464                StakingMsg::Undelegate {
2465                    validator: validator.to_string(),
2466                    amount: coin(111, "TOKEN"),
2467                },
2468            )
2469            .unwrap();
2470
2471            // slash validator
2472            test_env
2473                .router
2474                .staking
2475                .sudo(
2476                    &test_env.api,
2477                    &mut test_env.store,
2478                    &test_env.router,
2479                    &test_env.block,
2480                    StakingSudo::Slash {
2481                        validator: "testvaloper1".to_string(),
2482                        percentage: Decimal::percent(50),
2483                    },
2484                )
2485                .unwrap();
2486
2487            // query all delegations
2488            let response1: AllDelegationsResponse = query_stake(
2489                &test_env,
2490                StakingQuery::AllDelegations {
2491                    delegator: delegator.to_string(),
2492                },
2493            )
2494            .unwrap();
2495            assert_eq!(
2496                response1.delegations[0],
2497                Delegation {
2498                    delegator: delegator.clone(),
2499                    validator: validator.to_string(),
2500                    amount: coin(166, "TOKEN"),
2501                }
2502            );
2503        }
2504    }
2505}