1use crate::app::CosmosRouter;
2use crate::error::std_error_bail;
3use crate::executor::AppResponse;
4use crate::prefixed_storage::typed_prefixed_storage::{
5 StoragePrefix, TypedPrefixedStorage, TypedPrefixedStorageMut,
6};
7use crate::{BankSudo, Module};
8use cosmwasm_std::{
9 ensure, ensure_eq, to_json_binary, Addr, AllDelegationsResponse, AllValidatorsResponse, Api,
10 BankMsg, Binary, BlockInfo, BondedDenomResponse, Coin, CustomMsg, CustomQuery, Decimal256,
11 Delegation, DelegationResponse, DelegatorWithdrawAddressResponse, DistributionMsg,
12 DistributionQuery, Empty, Event, FullDelegation, Order, Querier, StakingMsg, StakingQuery,
13 StdError, StdResult, Storage, Timestamp, Uint256, Validator, ValidatorResponse,
14};
15#[cfg(feature = "cosmwasm_1_4")]
16use cosmwasm_std::{
17 DecCoin, DelegationRewardsResponse, DelegationTotalRewardsResponse, DelegatorReward,
18 DelegatorValidatorsResponse,
19};
20use cw_storage_plus::{Deque, Item, Map};
21use schemars::JsonSchema;
22use serde::{Deserialize, Serialize};
23use std::collections::{BTreeSet, VecDeque};
24
25const BONDED_DENOM: &str = "TOKEN";
27
28const YEAR: u64 = 60 * 60 * 24 * 365;
30
31#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
33pub struct StakingInfo {
34 pub bonded_denom: String,
36 pub unbonding_time: u64,
38 pub apr: Decimal256,
40}
41
42impl Default for StakingInfo {
43 fn default() -> Self {
45 StakingInfo {
46 bonded_denom: BONDED_DENOM.to_string(),
47 unbonding_time: 60,
48 apr: Decimal256::percent(10),
49 }
50 }
51}
52
53#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, JsonSchema)]
55struct Shares {
56 stake: Decimal256,
57 rewards: Decimal256,
58}
59
60impl Shares {
61 pub fn share_of_rewards(
63 &self,
64 validator_info: &ValidatorInfo,
65 rewards: Decimal256,
66 ) -> Decimal256 {
67 if validator_info.stake.is_zero() {
68 return Decimal256::zero();
69 }
70 rewards * self.stake / validator_info.stake
71 }
72}
73
74#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
76struct ValidatorInfo {
77 stakers: BTreeSet<Addr>,
80 stake: Uint256,
82 last_rewards_calculation: Timestamp,
84}
85
86impl ValidatorInfo {
87 pub fn new(block_time: Timestamp) -> Self {
88 Self {
89 stakers: BTreeSet::new(),
90 stake: Uint256::zero(),
91 last_rewards_calculation: block_time,
92 }
93 }
94}
95
96#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
97struct Unbonding {
98 pub delegator: Addr,
100 pub validator: String,
102 pub amount: Uint256,
104 pub payout_at: Timestamp,
106}
107
108const STAKING_INFO: Item<StakingInfo> = Item::new("staking_info");
109const STAKES: Map<(&Addr, &str), Shares> = Map::new("stakes");
111const VALIDATOR_MAP: Map<&str, Validator> = Map::new("validator_map");
112const VALIDATORS: Deque<Validator> = Deque::new("validators");
114const VALIDATOR_INFO: Map<&str, ValidatorInfo> = Map::new("validator_info");
116const UNBONDING_QUEUE: Item<VecDeque<Unbonding>> = Item::new("unbonding_queue");
118const WITHDRAW_ADDRESS: Map<&Addr, Addr> = Map::new("withdraw_address");
122
123#[derive(Clone, Debug, PartialEq, Eq, JsonSchema)]
127pub enum StakingSudo {
128 Slash {
131 validator: String,
133 percentage: Decimal256,
135 },
136}
137
138pub trait Staking: Module<ExecT = StakingMsg, QueryT = StakingQuery, SudoT = StakingSudo> {
143 fn process_queue<ExecC: CustomMsg, QueryC: CustomQuery>(
147 &self,
148 _api: &dyn Api,
149 _storage: &mut dyn Storage,
150 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
151 _block: &BlockInfo,
152 ) -> StdResult<AppResponse> {
153 Ok(AppResponse::default())
154 }
155}
156
157pub trait Distribution:
159 Module<ExecT = DistributionMsg, QueryT = DistributionQuery, SudoT = Empty>
160{
161}
162
163pub struct StakeKeeper {
165 module_addr: Addr,
167}
168
169impl Default for StakeKeeper {
170 fn default() -> Self {
172 StakeKeeper {
173 module_addr: Addr::unchecked("staking_module"),
175 }
176 }
177}
178
179impl StakeKeeper {
180 pub fn new() -> Self {
182 Self::default()
183 }
184
185 pub fn setup(&self, storage: &mut dyn Storage, staking_info: StakingInfo) -> StdResult<()> {
187 STAKING_INFO.save(&mut StakingStorageMut::new(storage), &staking_info)?;
188 Ok(())
189 }
190
191 pub fn add_validator(
193 &self,
194 _api: &dyn Api,
195 storage: &mut dyn Storage,
196 block: &BlockInfo,
197 validator: Validator,
198 ) -> StdResult<()> {
199 let mut storage = StakingStorageMut::new(storage);
200 if VALIDATOR_MAP
201 .may_load(&storage, &validator.address)?
202 .is_some()
203 {
204 std_error_bail!(
205 "Cannot add validator {}, since a validator with that address already exists",
206 validator.address
207 );
208 }
209 VALIDATOR_MAP.save(&mut storage, &validator.address, &validator)?;
210 VALIDATORS.push_back(&mut storage, &validator)?;
211 VALIDATOR_INFO.save(
212 &mut storage,
213 &validator.address,
214 &ValidatorInfo::new(block.time),
215 )?;
216 Ok(())
217 }
218
219 fn get_staking_info(storage: &StakingStorage) -> StdResult<StakingInfo> {
220 Ok(STAKING_INFO.may_load(storage)?.unwrap_or_default())
221 }
222
223 pub fn get_rewards(
225 &self,
226 storage: &dyn Storage,
227 block: &BlockInfo,
228 delegator: &Addr,
229 validator: &str,
230 ) -> StdResult<Option<Coin>> {
231 Self::get_rewards_internal(storage, block, delegator, validator)
232 }
233
234 fn get_rewards_internal(
235 storage: &dyn Storage,
236 block: &BlockInfo,
237 delegator: &Addr,
238 validator: &str,
239 ) -> StdResult<Option<Coin>> {
240 let staking_storage = StakingStorage::new(storage);
241 let validator_obj = match Self::get_validator(&staking_storage, validator)? {
242 Some(validator) => validator,
243 None => std_error_bail!("validator {} not found", validator),
244 };
245 let shares = match STAKES.load(&staking_storage, (delegator, validator)) {
247 Ok(stakes) => stakes,
248 Err(_) => return Ok(None),
249 };
250 let validator_info = VALIDATOR_INFO.load(&staking_storage, validator)?;
251 Self::get_rewards_from_validator(
252 &staking_storage,
253 block,
254 &shares,
255 &validator_obj,
256 &validator_info,
257 )
258 .map(Some)
259 }
260
261 fn get_rewards_from_validator(
262 storage: &StakingStorage,
263 block: &BlockInfo,
264 shares: &Shares,
265 validator: &Validator,
266 validator_info: &ValidatorInfo,
267 ) -> StdResult<Coin> {
268 let staking_info = Self::get_staking_info(storage)?;
269
270 let new_validator_rewards = Self::calculate_rewards(
272 block.time,
273 validator_info.last_rewards_calculation,
274 staking_info.apr,
275 validator.commission.into(),
276 validator_info.stake,
277 );
278
279 let delegator_rewards =
281 shares.rewards + shares.share_of_rewards(validator_info, new_validator_rewards);
282
283 Ok(Coin {
284 denom: staking_info.bonded_denom,
285 amount: Uint256::new(1).mul_floor(delegator_rewards), })
287 }
288
289 fn calculate_rewards(
291 current_time: Timestamp,
292 since: Timestamp,
293 interest_rate: Decimal256,
294 validator_commission: Decimal256,
295 stake: Uint256,
296 ) -> Decimal256 {
297 let time_diff = current_time.minus_seconds(since.seconds()).seconds();
299
300 let reward = Decimal256::from_ratio(stake, 1u128)
302 * interest_rate
303 * Decimal256::from_ratio(time_diff, 1u128)
304 / Decimal256::from_ratio(YEAR, 1u128);
305 let commission = reward * validator_commission;
306
307 reward - commission
308 }
309
310 fn update_rewards(
314 _api: &dyn Api,
315 storage: &mut StakingStorageMut,
316 block: &BlockInfo,
317 validator: &str,
318 ) -> StdResult<()> {
319 let staking_info = Self::get_staking_info(&storage.borrow())?;
320
321 let mut validator_info = VALIDATOR_INFO
322 .may_load(storage, validator)?
323 .ok_or_else(|| StdError::msg("validator does not exist"))?;
325
326 let validator_obj = VALIDATOR_MAP.load(storage, validator)?;
327
328 if validator_info.last_rewards_calculation >= block.time {
329 return Ok(());
330 }
331
332 let new_rewards = Self::calculate_rewards(
333 block.time,
334 validator_info.last_rewards_calculation,
335 staking_info.apr,
336 validator_obj.commission.into(),
337 validator_info.stake,
338 );
339
340 validator_info.last_rewards_calculation = block.time;
342 VALIDATOR_INFO.save(storage, validator, &validator_info)?;
343
344 if !new_rewards.is_zero() {
346 for staker in validator_info.stakers.iter() {
348 STAKES.update(
349 storage,
350 (staker, &validator_obj.address),
351 |shares| -> StdResult<_> {
352 let mut shares =
353 shares.expect("all stakers in validator_info should exist");
354 shares.rewards += shares.share_of_rewards(&validator_info, new_rewards);
355 Ok(shares)
356 },
357 )?;
358 }
359 }
360 Ok(())
361 }
362
363 fn get_validator(storage: &StakingStorage, address: &str) -> StdResult<Option<Validator>> {
365 VALIDATOR_MAP.may_load(storage, address)
366 }
367
368 fn get_validators(&self, storage: &StakingStorage) -> StdResult<Vec<Validator>> {
370 VALIDATORS.iter(storage)?.collect()
371 }
372
373 fn get_stake(
374 &self,
375 storage: &StakingStorage,
376 account: &Addr,
377 validator: &str,
378 ) -> StdResult<Option<Coin>> {
379 let shares = STAKES.may_load(storage, (account, validator))?;
380 let staking_info = Self::get_staking_info(storage)?;
381 Ok(shares.map(|shares| {
382 Coin {
383 denom: staking_info.bonded_denom,
384 amount: Uint256::new(1).mul_floor(shares.stake), }
386 }))
387 }
388
389 fn add_stake(
390 &self,
391 api: &dyn Api,
392 storage: &mut StakingStorageMut,
393 block: &BlockInfo,
394 to_address: &Addr,
395 validator: &str,
396 amount: Coin,
397 ) -> StdResult<()> {
398 self.validate_denom(&storage.borrow(), &amount)?;
399 self.update_stake(
400 api,
401 storage,
402 block,
403 to_address,
404 validator,
405 amount.amount,
406 false,
407 )
408 }
409
410 fn remove_stake(
411 &self,
412 api: &dyn Api,
413 storage: &mut StakingStorageMut,
414 block: &BlockInfo,
415 from_address: &Addr,
416 validator: &str,
417 amount: Coin,
418 ) -> StdResult<()> {
419 self.validate_denom(&storage.borrow(), &amount)?;
420 self.update_stake(
421 api,
422 storage,
423 block,
424 from_address,
425 validator,
426 amount.amount,
427 true,
428 )
429 }
430
431 fn update_stake(
432 &self,
433 api: &dyn Api,
434 storage: &mut StakingStorageMut,
435 block: &BlockInfo,
436 delegator: &Addr,
437 validator: &str,
438 amount: impl Into<Uint256>,
439 sub: bool,
440 ) -> StdResult<()> {
441 let amount = amount.into();
442
443 Self::update_rewards(api, storage, block, validator)?;
445
446 let mut validator_info = VALIDATOR_INFO
448 .may_load(storage, validator)?
449 .unwrap_or_else(|| ValidatorInfo::new(block.time));
450 let shares = STAKES.may_load(storage, (delegator, validator))?;
451 let mut shares = if sub {
452 shares.ok_or_else(|| StdError::msg("no delegation for (address, validator) tuple"))?
455 } else {
456 shares.unwrap_or_default()
457 };
458
459 let amount_dec = Decimal256::from_ratio(amount, 1u128);
460 if sub {
461 if amount_dec > shares.stake {
463 std_error_bail!("invalid shares amount");
464 }
465 shares.stake -= amount_dec;
466 validator_info.stake = validator_info.stake.checked_sub(amount)?;
467 } else {
468 shares.stake += amount_dec;
469 validator_info.stake = validator_info.stake.checked_add(amount)?;
470 }
471
472 if shares.stake.is_zero() {
474 STAKES.remove(storage, (delegator, validator));
476 validator_info.stakers.remove(delegator);
477 } else {
478 STAKES.save(storage, (delegator, validator), &shares)?;
479 validator_info.stakers.insert(delegator.clone());
480 }
481 VALIDATOR_INFO.save(storage, validator, &validator_info)?;
483
484 Ok(())
485 }
486
487 fn slash(
488 &self,
489 api: &dyn Api,
490 storage: &mut StakingStorageMut,
491 block: &BlockInfo,
492 validator: &str,
493 percentage: Decimal256,
494 ) -> StdResult<()> {
495 Self::update_rewards(api, storage, block, validator)?;
497
498 let mut validator_info = VALIDATOR_INFO.may_load(storage, validator)?.unwrap();
500
501 let remaining_percentage = Decimal256::one() - percentage;
502 validator_info.stake = validator_info.stake.mul_floor(remaining_percentage);
503
504 if validator_info.stake.is_zero() {
506 for delegator in validator_info.stakers.iter() {
508 STAKES.remove(storage, (delegator, validator));
509 }
510 validator_info.stakers.clear();
511 } else {
512 for delegator in validator_info.stakers.iter() {
514 STAKES.update(storage, (delegator, validator), |stake| -> StdResult<_> {
515 let mut stake = stake.expect("all stakers in validator_info should exist");
516 stake.stake *= remaining_percentage;
517
518 Ok(stake)
519 })?;
520 }
521 }
522 let mut unbonding_queue = UNBONDING_QUEUE.may_load(storage)?.unwrap_or_default();
524 unbonding_queue
525 .iter_mut()
526 .filter(|ub| ub.validator == validator)
527 .for_each(|ub| {
528 ub.amount = ub.amount.mul_floor(remaining_percentage);
529 });
530 UNBONDING_QUEUE.save(storage, &unbonding_queue)?;
531
532 VALIDATOR_INFO.save(storage, validator, &validator_info)?;
533 Ok(())
534 }
535
536 fn validate_denom(&self, storage: &StakingStorage, amount: &Coin) -> StdResult<()> {
538 let staking_info = Self::get_staking_info(storage)?;
539 ensure_eq!(
540 amount.denom,
541 staking_info.bonded_denom,
542 StdError::msg(format!(
543 "cannot delegate coins of denominator {}, only of {}",
544 amount.denom, staking_info.bonded_denom
545 ))
546 );
547 Ok(())
548 }
549
550 fn validate_percentage(&self, percentage: Decimal256) -> StdResult<()> {
552 ensure!(
553 percentage <= Decimal256::one(),
554 StdError::msg("expected percentage")
555 );
556 Ok(())
557 }
558
559 fn process_queue<ExecC: CustomMsg, QueryC: CustomQuery>(
560 &self,
561 api: &dyn Api,
562 storage: &mut dyn Storage,
563 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
564 block: &BlockInfo,
565 ) -> StdResult<AppResponse> {
566 let mut unbonding_queue = UNBONDING_QUEUE
567 .may_load(&StakingStorage::new(storage))?
568 .unwrap_or_default();
569 loop {
570 match unbonding_queue.front() {
571 Some(Unbonding { payout_at, .. }) if payout_at <= &block.time => {
573 let mut staking_storage_mut = StakingStorageMut::new(storage);
574
575 let Unbonding {
577 delegator,
578 validator,
579 amount,
580 ..
581 } = unbonding_queue.pop_front().unwrap();
582
583 let delegation = self
585 .get_stake(&staking_storage_mut.borrow(), &delegator, &validator)?
586 .map(|mut stake| {
587 stake.amount += unbonding_queue
589 .iter()
590 .filter(|u| u.delegator == delegator && u.validator == validator)
591 .map(|u| u.amount)
592 .sum::<Uint256>();
593 stake
594 });
595 match delegation {
596 Some(delegation) if delegation.amount.is_zero() => {
597 STAKES.remove(&mut staking_storage_mut, (&delegator, &validator));
598 }
599 None => {
600 STAKES.remove(&mut staking_storage_mut, (&delegator, &validator));
601 }
602 _ => {}
603 }
604
605 let staking_info = Self::get_staking_info(&staking_storage_mut.borrow())?;
606 if !amount.is_zero() {
607 router.execute(
608 api,
609 storage,
610 block,
611 self.module_addr.clone(),
612 BankMsg::Send {
613 to_address: delegator.into_string(),
614 amount: vec![Coin::new(amount, &staking_info.bonded_denom)],
615 }
616 .into(),
617 )?;
618 }
619 }
620 _ => break,
621 }
622 }
623 UNBONDING_QUEUE.save(&mut StakingStorageMut::new(storage), &unbonding_queue)?;
624 Ok(AppResponse::default())
625 }
626}
627
628impl Staking for StakeKeeper {
629 fn process_queue<ExecC: CustomMsg, QueryC: CustomQuery>(
630 &self,
631 api: &dyn Api,
632 storage: &mut dyn Storage,
633 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
634 block: &BlockInfo,
635 ) -> StdResult<AppResponse> {
636 self.process_queue(api, storage, router, block)
637 }
638}
639
640impl StoragePrefix for StakeKeeper {
641 const NAMESPACE: &'static [u8] = b"staking";
642}
643type StakingStorage<'a> = TypedPrefixedStorage<'a, StakeKeeper>;
644type StakingStorageMut<'a> = TypedPrefixedStorageMut<'a, StakeKeeper>;
645
646impl Module for StakeKeeper {
647 type ExecT = StakingMsg;
648 type QueryT = StakingQuery;
649 type SudoT = StakingSudo;
650
651 fn execute<ExecC: CustomMsg, QueryC: CustomQuery>(
652 &self,
653 api: &dyn Api,
654 storage: &mut dyn Storage,
655 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
656 block: &BlockInfo,
657 sender: Addr,
658 msg: StakingMsg,
659 ) -> StdResult<AppResponse> {
660 let mut staking_storage_mut = StakingStorageMut::new(storage);
661 match msg {
662 StakingMsg::Delegate { validator, amount } => {
663 if amount.amount.is_zero() {
665 std_error_bail!("invalid delegation amount");
666 }
667
668 let events = vec![Event::new("delegate")
670 .add_attribute("validator", &validator)
671 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))
672 .add_attribute("new_shares", amount.amount.to_string())]; self.add_stake(
674 api,
675 &mut staking_storage_mut,
676 block,
677 &sender,
678 &validator,
679 amount.clone(),
680 )?;
681 router.execute(
683 api,
684 storage,
685 block,
686 sender,
687 BankMsg::Send {
688 to_address: self.module_addr.to_string(),
689 amount: vec![amount],
690 }
691 .into(),
692 )?;
693 Ok(AppResponse {
694 events,
695 ..Default::default()
696 })
697 }
698 StakingMsg::Undelegate { validator, amount } => {
699 self.validate_denom(&staking_storage_mut.borrow(), &amount)?;
700
701 if amount.amount.is_zero() {
703 std_error_bail!("invalid shares amount");
704 }
705
706 let events = vec![Event::new("unbond")
708 .add_attribute("validator", &validator)
709 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))
710 .add_attribute("completion_time", "2022-09-27T14:00:00+00:00")]; self.remove_stake(
712 api,
713 &mut staking_storage_mut,
714 block,
715 &sender,
716 &validator,
717 amount.clone(),
718 )?;
719 let staking_info = Self::get_staking_info(&staking_storage_mut.borrow())?;
721 let mut unbonding_queue = UNBONDING_QUEUE
722 .may_load(&staking_storage_mut)?
723 .unwrap_or_default();
724 unbonding_queue.push_back(Unbonding {
725 delegator: sender.clone(),
726 validator,
727 amount: amount.amount,
728 payout_at: block.time.plus_seconds(staking_info.unbonding_time),
729 });
730 UNBONDING_QUEUE.save(&mut staking_storage_mut, &unbonding_queue)?;
731 Ok(AppResponse {
732 events,
733 ..Default::default()
734 })
735 }
736 StakingMsg::Redelegate {
737 src_validator,
738 dst_validator,
739 amount,
740 } => {
741 let events = vec![Event::new("redelegate")
743 .add_attribute("source_validator", &src_validator)
744 .add_attribute("destination_validator", &dst_validator)
745 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))];
746
747 self.remove_stake(
748 api,
749 &mut staking_storage_mut,
750 block,
751 &sender,
752 &src_validator,
753 amount.clone(),
754 )?;
755 self.add_stake(
756 api,
757 &mut staking_storage_mut,
758 block,
759 &sender,
760 &dst_validator,
761 amount,
762 )?;
763
764 Ok(AppResponse {
765 events,
766 ..Default::default()
767 })
768 }
769 m => std_error_bail!("Unsupported staking message: {:?}", m),
770 }
771 }
772
773 fn query(
774 &self,
775 api: &dyn Api,
776 storage: &dyn Storage,
777 _querier: &dyn Querier,
778 block: &BlockInfo,
779 request: StakingQuery,
780 ) -> StdResult<Binary> {
781 let staking_storage = StakingStorage::new(storage);
782 match request {
783 StakingQuery::BondedDenom {} => Ok(to_json_binary(&BondedDenomResponse::new(
784 Self::get_staking_info(&staking_storage)?.bonded_denom,
785 ))?),
786 StakingQuery::AllDelegations { delegator } => {
787 let delegator = api.addr_validate(&delegator)?;
788 let validators = self.get_validators(&staking_storage)?;
789
790 let res: StdResult<Vec<Delegation>> =
791 validators
792 .into_iter()
793 .filter_map(|validator| {
794 let delegator = delegator.clone();
795 let amount = self
796 .get_stake(&staking_storage, &delegator, &validator.address)
797 .transpose()?;
798
799 Some(amount.map(|amount| {
800 Delegation::new(delegator, validator.address, amount)
801 }))
802 })
803 .collect();
804
805 Ok(to_json_binary(&AllDelegationsResponse::new(res?))?)
806 }
807 StakingQuery::Delegation {
808 delegator,
809 validator,
810 } => {
811 let validator_obj = match Self::get_validator(&staking_storage, &validator)? {
812 Some(validator) => validator,
813 None => std_error_bail!("non-existent validator {}", validator),
814 };
815 let delegator = api.addr_validate(&delegator)?;
816
817 let shares = STAKES
818 .may_load(&staking_storage, (&delegator, &validator))?
819 .unwrap_or_default();
820
821 let validator_info = VALIDATOR_INFO.load(&staking_storage, &validator)?;
822 let reward = Self::get_rewards_from_validator(
823 &staking_storage,
824 block,
825 &shares,
826 &validator_obj,
827 &validator_info,
828 )?;
829 let staking_info = Self::get_staking_info(&staking_storage)?;
830
831 let amount = Coin::new(
832 Uint256::new(1).mul_floor(shares.stake),
833 staking_info.bonded_denom,
834 );
835
836 let full_delegation_response = if amount.amount.is_zero() {
837 DelegationResponse::new(None)
839 } else {
840 DelegationResponse::new(Some(FullDelegation::new(
841 delegator,
842 validator,
843 amount.clone(),
844 amount, if reward.amount.is_zero() {
846 vec![]
847 } else {
848 vec![reward]
849 },
850 )))
851 };
852
853 let res = to_json_binary(&full_delegation_response)?;
854 Ok(res)
855 }
856 StakingQuery::AllValidators {} => {
857 let validators: Vec<Validator> = self.get_validators(&staking_storage)?;
858 Ok(to_json_binary(&AllValidatorsResponse::new(
859 validators.into_iter().map(Into::into).collect(),
860 ))?)
861 }
862 StakingQuery::Validator { address } => Ok(to_json_binary(&ValidatorResponse::new(
863 Self::get_validator(&staking_storage, &address)?,
864 ))?),
865 q => std_error_bail!("Unsupported staking sudo message: {:?}", q),
866 }
867 }
868
869 fn sudo<ExecC: CustomMsg, QueryC: CustomQuery>(
870 &self,
871 api: &dyn Api,
872 storage: &mut dyn Storage,
873 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
874 block: &BlockInfo,
875 msg: StakingSudo,
876 ) -> StdResult<AppResponse> {
877 match msg {
878 StakingSudo::Slash {
879 validator,
880 percentage,
881 } => {
882 let mut staking_storage = StakingStorageMut::new(storage);
883 self.validate_percentage(percentage)?;
884 self.slash(api, &mut staking_storage, block, &validator, percentage)?;
885 Ok(AppResponse::default())
886 }
887 }
888 }
889}
890
891#[derive(Default)]
897pub struct DistributionKeeper {}
898
899impl DistributionKeeper {
900 pub fn new() -> Self {
902 Self::default()
903 }
904
905 pub fn remove_rewards(
907 &self,
908 api: &dyn Api,
909 storage: &mut dyn Storage,
910 block: &BlockInfo,
911 delegator: &Addr,
912 validator: &str,
913 ) -> StdResult<Uint256> {
914 let mut staking_storage_mut = StakingStorageMut::new(storage);
915 StakeKeeper::update_rewards(api, &mut staking_storage_mut, block, validator)?;
917
918 let mut shares = STAKES.load(&staking_storage_mut, (delegator, validator))?;
920 let rewards = Uint256::new(1).mul_floor(shares.rewards); shares.rewards = Decimal256::zero();
924 STAKES.save(&mut staking_storage_mut, (delegator, validator), &shares)?;
925
926 Ok(rewards)
927 }
928
929 pub fn get_withdraw_address(storage: &dyn Storage, delegator_addr: &Addr) -> StdResult<Addr> {
931 let storage = DistributionStorage::new(storage);
932 Ok(match WITHDRAW_ADDRESS.may_load(&storage, delegator_addr)? {
933 Some(withdraw_addr) => withdraw_addr,
934 None => delegator_addr.clone(),
935 })
936 }
937
938 pub fn set_withdraw_address(
942 storage: &mut dyn Storage,
943 delegator_addr: &Addr,
944 withdraw_addr: &Addr,
945 ) -> StdResult<()> {
946 let mut storage = DistributionStorageMut::new(storage);
947 if delegator_addr == withdraw_addr {
948 WITHDRAW_ADDRESS.remove(&mut storage, delegator_addr);
949 Ok(())
950 } else {
951 WITHDRAW_ADDRESS.save(&mut storage, delegator_addr, withdraw_addr)
953 }
954 }
955
956 pub fn get_delegator_validators(
958 &self,
959 storage: &dyn Storage,
960 delegator_addr: &Addr,
961 ) -> StdResult<Vec<String>> {
962 let storage = StakingStorage::new(storage);
963 STAKES
964 .prefix(delegator_addr)
965 .keys(&storage, None, None, Order::Ascending)
966 .collect::<Result<Vec<String>, StdError>>()
967 }
968
969 #[cfg(feature = "cosmwasm_1_4")]
971 pub fn get_rewards(
972 &self,
973 storage: &dyn Storage,
974 block: &BlockInfo,
975 delegator_address: &Addr,
976 validator_address: &str,
977 ) -> StdResult<Option<DecCoin>> {
978 Ok(
979 if let Some(coin) = StakeKeeper::get_rewards_internal(
980 storage,
981 block,
982 delegator_address,
983 validator_address,
984 )? {
985 Some(DecCoin::new(
986 Decimal256::from_atomics(coin.amount, 0)?,
987 coin.denom,
988 ))
989 } else {
990 None
991 },
992 )
993 }
994}
995
996impl Distribution for DistributionKeeper {}
997
998impl StoragePrefix for DistributionKeeper {
999 const NAMESPACE: &'static [u8] = b"distribution";
1000}
1001type DistributionStorage<'a> = TypedPrefixedStorage<'a, DistributionKeeper>;
1002type DistributionStorageMut<'a> = TypedPrefixedStorageMut<'a, DistributionKeeper>;
1003
1004impl Module for DistributionKeeper {
1005 type ExecT = DistributionMsg;
1006 type QueryT = DistributionQuery;
1007 type SudoT = Empty;
1008
1009 fn execute<ExecC: CustomMsg, QueryC: CustomQuery>(
1010 &self,
1011 api: &dyn Api,
1012 storage: &mut dyn Storage,
1013 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1014 block: &BlockInfo,
1015 sender: Addr,
1016 msg: DistributionMsg,
1017 ) -> StdResult<AppResponse> {
1018 match msg {
1019 DistributionMsg::WithdrawDelegatorReward { validator } => {
1020 let rewards = self.remove_rewards(api, storage, block, &sender, &validator)?;
1021 let staking_storage = StakingStorage::new(storage);
1022 let staking_info = StakeKeeper::get_staking_info(&staking_storage)?;
1023 let receiver = Self::get_withdraw_address(storage, &sender)?;
1024 router.sudo(
1026 api,
1027 storage,
1028 block,
1029 BankSudo::Mint {
1030 to_address: receiver.into_string(),
1031 amount: vec![Coin {
1032 amount: rewards,
1033 denom: staking_info.bonded_denom.clone(),
1034 }],
1035 }
1036 .into(),
1037 )?;
1038
1039 let events = vec![Event::new("withdraw_delegator_reward")
1040 .add_attribute("validator", &validator)
1041 .add_attribute("sender", &sender)
1042 .add_attribute(
1043 "amount",
1044 format!("{}{}", rewards, staking_info.bonded_denom),
1045 )];
1046 Ok(AppResponse {
1047 events,
1048 ..Default::default()
1049 })
1050 }
1051 DistributionMsg::SetWithdrawAddress { address } => {
1052 let address = api.addr_validate(&address)?;
1053 Self::set_withdraw_address(storage, &sender, &address)?;
1055 Ok(AppResponse {
1056 events: vec![Event::new("set_withdraw_address")
1058 .add_attribute("withdraw_address", address)],
1059 ..Default::default()
1060 })
1061 }
1062 other => std_error_bail!("Unsupported distribution message: {:?}", other),
1063 }
1064 }
1065
1066 fn query(
1067 &self,
1068 api: &dyn Api,
1069 storage: &dyn Storage,
1070 _querier: &dyn Querier,
1071 block: &BlockInfo,
1072 request: DistributionQuery,
1073 ) -> StdResult<Binary> {
1074 match request {
1075 #[cfg(feature = "cosmwasm_1_4")]
1076 DistributionQuery::DelegatorValidators { delegator_address } => {
1077 let delegator_address = api.addr_validate(&delegator_address)?;
1078 let validators = self.get_delegator_validators(storage, &delegator_address)?;
1079 Ok(to_json_binary(&DelegatorValidatorsResponse::new(
1080 validators,
1081 ))?)
1082 }
1083 DistributionQuery::DelegatorWithdrawAddress { delegator_address } => {
1084 let delegator_address = api.addr_validate(&delegator_address)?;
1085 let withdraw_address = Self::get_withdraw_address(storage, &delegator_address)?;
1086 Ok(to_json_binary(&DelegatorWithdrawAddressResponse::new(
1087 withdraw_address,
1088 ))?)
1089 }
1090 #[cfg(feature = "cosmwasm_1_4")]
1091 DistributionQuery::DelegationRewards {
1092 delegator_address,
1093 validator_address,
1094 } => {
1095 let delegator_address = api.addr_validate(&delegator_address)?;
1096 let rewards = if let Some(dec_coin) =
1097 self.get_rewards(storage, block, &delegator_address, &validator_address)?
1098 {
1099 vec![dec_coin]
1100 } else {
1101 vec![]
1102 };
1103 Ok(to_json_binary(&DelegationRewardsResponse::new(rewards))?)
1104 }
1105 #[cfg(feature = "cosmwasm_1_4")]
1106 DistributionQuery::DelegationTotalRewards { delegator_address } => {
1107 let delegator_address = api.addr_validate(&delegator_address)?;
1108 let mut delegator_rewards = vec![];
1109 let mut total_rewards = std::collections::BTreeMap::new();
1110 for validator_address in
1111 self.get_delegator_validators(storage, &delegator_address)?
1112 {
1113 if let Some(dec_coin) =
1114 self.get_rewards(storage, block, &delegator_address, &validator_address)?
1115 {
1116 delegator_rewards.push(DelegatorReward::new(
1117 validator_address.clone(),
1118 vec![dec_coin.clone()],
1119 ));
1120 total_rewards
1121 .entry(dec_coin.denom)
1122 .and_modify(|value| *value += dec_coin.amount)
1123 .or_insert(dec_coin.amount);
1124 }
1125 }
1126 let total_rewards = total_rewards
1127 .iter()
1128 .map(|(denom, amount)| DecCoin::new(*amount, denom))
1129 .collect();
1130 Ok(to_json_binary(&DelegationTotalRewardsResponse::new(
1131 delegator_rewards,
1132 total_rewards,
1133 ))?)
1134 }
1135 other => {
1136 let _ = block; std_error_bail!("Unsupported distribution query: {:?}", other)
1138 }
1139 }
1140 }
1141
1142 fn sudo<ExecC, QueryC>(
1143 &self,
1144 _api: &dyn Api,
1145 _storage: &mut dyn Storage,
1146 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1147 _block: &BlockInfo,
1148 _msg: Empty,
1149 ) -> StdResult<AppResponse> {
1150 std_error_bail!("Something went wrong - distribution doesn't have sudo messages")
1151 }
1152}
1153
1154#[cfg(test)]
1155mod test {
1156 use super::*;
1157 use crate::{
1158 BankKeeper, FailingModule, GovFailingModule, IbcFailingModule, IntoBech32, Router,
1159 StargateFailing, WasmKeeper,
1160 };
1161 use cosmwasm_std::{
1162 coin, coins, from_json,
1163 testing::{mock_env, MockApi, MockStorage},
1164 BalanceResponse, BankQuery, Decimal, QuerierWrapper, Uint256,
1165 };
1166 use serde::de::DeserializeOwned;
1167
1168 struct ValidatorProperties {
1171 commission: Decimal,
1173 max_commission: Decimal,
1175 max_change_rate: Decimal,
1177 }
1178
1179 fn vp(commission: u64, max_commission: u64, max_change_rate: u64) -> ValidatorProperties {
1181 ValidatorProperties {
1182 commission: Decimal::percent(commission),
1183 max_commission: Decimal::percent(max_commission),
1184 max_change_rate: Decimal::percent(max_change_rate),
1185 }
1186 }
1187
1188 type BasicRouter<ExecC = Empty, QueryC = Empty> = Router<
1190 BankKeeper,
1191 FailingModule<ExecC, QueryC, Empty>,
1192 WasmKeeper<ExecC, QueryC>,
1193 StakeKeeper,
1194 DistributionKeeper,
1195 IbcFailingModule,
1196 GovFailingModule,
1197 StargateFailing,
1198 >;
1199
1200 struct TestEnv {
1202 api: MockApi,
1203 storage: MockStorage,
1204 router: BasicRouter,
1205 block: BlockInfo,
1206 validator_addr_1: String,
1207 validator_addr_2: String,
1208 validator_addr_3: String,
1209 delegator_addr_1: Addr,
1210 delegator_addr_2: Addr,
1211 user_addr_1: Addr,
1212 }
1213
1214 impl TestEnv {
1215 fn new(validator1: ValidatorProperties, validator2: ValidatorProperties) -> Self {
1217 fn validator_address(value: &str) -> String {
1220 value.into_bech32_with_prefix("cosmwasmvaloper").to_string()
1221 }
1222
1223 fn user_address(api: &MockApi, value: &str) -> Addr {
1226 api.addr_make(value)
1227 }
1228
1229 let api = MockApi::default();
1230 let router = Router {
1231 wasm: WasmKeeper::new(),
1232 bank: BankKeeper::new(),
1233 custom: FailingModule::new(),
1234 staking: StakeKeeper::new(),
1235 distribution: DistributionKeeper::new(),
1236 ibc: IbcFailingModule::new(),
1237 gov: GovFailingModule::new(),
1238 stargate: StargateFailing,
1239 };
1240 let mut storage = MockStorage::new();
1241 let block = mock_env().block;
1242
1243 let validator_addr_1 = validator_address("validator1");
1244 let validator_addr_2 = validator_address("validator2");
1245 let validator_addr_3 = validator_address("validator3");
1246
1247 router
1249 .staking
1250 .setup(&mut storage, StakingInfo::default())
1251 .unwrap();
1252
1253 let valoper1 = Validator::new(
1255 validator_addr_1.to_string(),
1256 validator1.commission,
1257 validator1.max_commission,
1258 validator1.max_change_rate,
1259 );
1260 router
1261 .staking
1262 .add_validator(&api, &mut storage, &block, valoper1)
1263 .unwrap();
1264
1265 let valoper2 = Validator::new(
1267 validator_addr_2.to_string(),
1268 validator2.commission,
1269 validator2.max_commission,
1270 validator2.max_change_rate,
1271 );
1272 router
1273 .staking
1274 .add_validator(&api, &mut storage, &block, valoper2)
1275 .unwrap();
1276
1277 Self {
1279 api,
1280 storage,
1281 router,
1282 block,
1283 validator_addr_1,
1284 validator_addr_2,
1285 validator_addr_3,
1286 delegator_addr_1: user_address(&api, "delegator1"),
1287 delegator_addr_2: user_address(&api, "delegator2"),
1288 user_addr_1: user_address(&api, "user1"),
1289 }
1290 }
1291
1292 #[inline(always)]
1294 fn validator_addr_1(&self) -> String {
1295 self.validator_addr_1.clone()
1296 }
1297
1298 #[inline(always)]
1300 fn validator_addr_2(&self) -> String {
1301 self.validator_addr_2.clone()
1302 }
1303
1304 #[inline(always)]
1306 fn validator_addr_3(&self) -> String {
1307 self.validator_addr_3.clone()
1308 }
1309
1310 #[inline(always)]
1312 fn delegator_addr_1(&self) -> Addr {
1313 self.delegator_addr_1.clone()
1314 }
1315
1316 #[inline(always)]
1318 fn delegator_addr_2(&self) -> Addr {
1319 self.delegator_addr_2.clone()
1320 }
1321
1322 #[inline(always)]
1324 fn user_addr_1(&self) -> Addr {
1325 self.user_addr_1.clone()
1326 }
1327 }
1328
1329 fn execute_stake(env: &mut TestEnv, sender: Addr, msg: StakingMsg) -> StdResult<AppResponse> {
1331 env.router.staking.execute(
1332 &env.api,
1333 &mut env.storage,
1334 &env.router,
1335 &env.block,
1336 sender,
1337 msg,
1338 )
1339 }
1340
1341 fn query_stake<T: DeserializeOwned>(env: &TestEnv, msg: StakingQuery) -> StdResult<T> {
1343 from_json(env.router.staking.query(
1344 &env.api,
1345 &env.storage,
1346 &env.router.querier(&env.api, &env.storage, &env.block),
1347 &env.block,
1348 msg,
1349 )?)
1350 }
1351
1352 fn execute_distr(
1354 env: &mut TestEnv,
1355 sender: Addr,
1356 msg: DistributionMsg,
1357 ) -> StdResult<AppResponse> {
1358 env.router.distribution.execute(
1359 &env.api,
1360 &mut env.storage,
1361 &env.router,
1362 &env.block,
1363 sender,
1364 msg,
1365 )
1366 }
1367
1368 fn query_bank<T: DeserializeOwned>(env: &TestEnv, msg: BankQuery) -> StdResult<T> {
1370 from_json(env.router.bank.query(
1371 &env.api,
1372 &env.storage,
1373 &env.router.querier(&env.api, &env.storage, &env.block),
1374 &env.block,
1375 msg,
1376 )?)
1377 }
1378
1379 fn init_balance(env: &mut TestEnv, address: &Addr, amount: u128) {
1381 init_balance_denom(env, address, amount, BONDED_DENOM);
1382 }
1383
1384 fn init_balance_denom(env: &mut TestEnv, address: &Addr, amount: u128, denom: &str) {
1386 env.router
1387 .bank
1388 .init_balance(&mut env.storage, address, coins(amount, denom))
1389 .unwrap();
1390 }
1391
1392 fn assert_balances(env: &TestEnv, balances: impl IntoIterator<Item = (Addr, u128)>) {
1394 for (addr, amount) in balances {
1395 let balance: BalanceResponse = query_bank(
1396 env,
1397 BankQuery::Balance {
1398 address: addr.to_string(),
1399 denom: BONDED_DENOM.to_string(),
1400 },
1401 )
1402 .unwrap();
1403 assert_eq!(balance.amount.amount, Uint256::new(amount));
1404 }
1405 }
1406
1407 #[test]
1408 fn add_get_validators() {
1409 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 20, 1));
1410
1411 let validator_addr_3 = env.validator_addr_3();
1412
1413 let validator = Validator::new(
1415 validator_addr_3.to_string(),
1416 Decimal::percent(1),
1417 Decimal::percent(10),
1418 Decimal::percent(1),
1419 );
1420 env.router
1421 .staking
1422 .add_validator(&env.api, &mut env.storage, &env.block, validator.clone())
1423 .unwrap();
1424
1425 let val = StakeKeeper::get_validator(&StakingStorage::new(&env.storage), &validator_addr_3)
1427 .unwrap()
1428 .unwrap();
1429 assert_eq!(val, validator);
1430
1431 let validator_fake = Validator::new(
1433 validator_addr_3.to_string(),
1434 Decimal::percent(2),
1435 Decimal::percent(20),
1436 Decimal::percent(2),
1437 );
1438 env.router
1439 .staking
1440 .add_validator(&env.api, &mut env.storage, &env.block, validator_fake)
1441 .unwrap_err();
1442
1443 let val = StakeKeeper::get_validator(&StakingStorage::new(&env.storage), &validator_addr_3)
1445 .unwrap()
1446 .unwrap();
1447 assert_eq!(val, validator);
1448 }
1449
1450 #[test]
1451 fn validator_slashing() {
1452 let mut env = TestEnv::new(vp(10, 20, 1), vp(10, 20, 1));
1453
1454 let validator_addr_1 = env.validator_addr_1();
1455 let delegator_addr_1 = env.delegator_addr_1();
1456
1457 let mut staking_storage_mut = StakingStorageMut::new(&mut env.storage);
1459 env.router
1460 .staking
1461 .add_stake(
1462 &env.api,
1463 &mut staking_storage_mut,
1464 &env.block,
1465 &delegator_addr_1,
1466 &validator_addr_1,
1467 coin(100, BONDED_DENOM),
1468 )
1469 .unwrap();
1470
1471 env.router
1473 .staking
1474 .sudo(
1475 &env.api,
1476 &mut env.storage,
1477 &env.router,
1478 &env.block,
1479 StakingSudo::Slash {
1480 validator: validator_addr_1.to_string(),
1481 percentage: Decimal256::percent(50),
1482 },
1483 )
1484 .unwrap();
1485
1486 let stake_left = env
1488 .router
1489 .staking
1490 .get_stake(
1491 &StakingStorage::new(&env.storage),
1492 &delegator_addr_1,
1493 &validator_addr_1,
1494 )
1495 .unwrap()
1496 .unwrap();
1497 assert_eq!(Uint256::new(50), stake_left.amount);
1498
1499 env.router
1501 .staking
1502 .sudo(
1503 &env.api,
1504 &mut env.storage,
1505 &env.router,
1506 &env.block,
1507 StakingSudo::Slash {
1508 validator: validator_addr_1.to_string(),
1509 percentage: Decimal256::percent(100),
1510 },
1511 )
1512 .unwrap();
1513
1514 let stake_left = env
1516 .router
1517 .staking
1518 .get_stake(
1519 &StakingStorage::new(&env.storage),
1520 &delegator_addr_1,
1521 &validator_addr_1,
1522 )
1523 .unwrap();
1524 assert_eq!(None, stake_left);
1525 }
1526
1527 #[test]
1528 fn rewards_work_for_single_delegator() {
1529 let mut env = TestEnv::new(vp(10, 20, 1), vp(10, 20, 1));
1530
1531 let validator_addr_1 = env.validator_addr_1();
1532 let delegator_addr_1 = env.delegator_addr_1();
1533
1534 let mut staking_storage_mut = StakingStorageMut::new(&mut env.storage);
1535 env.router
1537 .staking
1538 .add_stake(
1539 &env.api,
1540 &mut staking_storage_mut,
1541 &env.block,
1542 &delegator_addr_1,
1543 &validator_addr_1,
1544 coin(200, BONDED_DENOM),
1545 )
1546 .unwrap();
1547
1548 env.block.time = env.block.time.plus_seconds(YEAR / 2);
1550
1551 let rewards = env
1553 .router
1554 .staking
1555 .get_rewards(
1556 &env.storage,
1557 &env.block,
1558 &delegator_addr_1,
1559 &validator_addr_1,
1560 )
1561 .unwrap()
1562 .unwrap();
1563 assert_eq!(Uint256::new(9), rewards.amount);
1564
1565 env.router
1567 .distribution
1568 .execute(
1569 &env.api,
1570 &mut env.storage,
1571 &env.router,
1572 &env.block,
1573 delegator_addr_1.clone(),
1574 DistributionMsg::WithdrawDelegatorReward {
1575 validator: validator_addr_1.to_string(),
1576 },
1577 )
1578 .unwrap();
1579
1580 let rewards = env
1582 .router
1583 .staking
1584 .get_rewards(
1585 &env.storage,
1586 &env.block,
1587 &delegator_addr_1,
1588 &validator_addr_1,
1589 )
1590 .unwrap()
1591 .unwrap();
1592 assert_eq!(Uint256::zero(), rewards.amount);
1593
1594 env.block.time = env.block.time.plus_seconds(YEAR / 2);
1596 let rewards = env
1598 .router
1599 .staking
1600 .get_rewards(
1601 &env.storage,
1602 &env.block,
1603 &delegator_addr_1,
1604 &validator_addr_1,
1605 )
1606 .unwrap()
1607 .unwrap();
1608 assert_eq!(Uint256::new(9), rewards.amount);
1609 }
1610
1611 #[test]
1612 fn rewards_work_for_multiple_delegators() {
1613 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
1614
1615 let validator_addr_1 = env.validator_addr_1();
1616 let delegator_addr_1 = env.delegator_addr_1();
1617 let delegator_addr_2 = env.delegator_addr_2();
1618
1619 env.router
1621 .staking
1622 .add_stake(
1623 &env.api,
1624 &mut StakingStorageMut::new(&mut env.storage),
1625 &env.block,
1626 &delegator_addr_1,
1627 &validator_addr_1,
1628 coin(100, BONDED_DENOM),
1629 )
1630 .unwrap();
1631 env.router
1632 .staking
1633 .add_stake(
1634 &env.api,
1635 &mut StakingStorageMut::new(&mut env.storage),
1636 &env.block,
1637 &delegator_addr_2,
1638 &validator_addr_1,
1639 coin(200, BONDED_DENOM),
1640 )
1641 .unwrap();
1642
1643 env.block.time = env.block.time.plus_seconds(YEAR);
1645
1646 let rewards = env
1648 .router
1649 .staking
1650 .get_rewards(
1651 &env.storage,
1652 &env.block,
1653 &delegator_addr_1,
1654 &validator_addr_1,
1655 )
1656 .unwrap()
1657 .unwrap();
1658 assert_eq!(Uint256::new(9), rewards.amount);
1659
1660 let rewards = env
1662 .router
1663 .staking
1664 .get_rewards(
1665 &env.storage,
1666 &env.block,
1667 &delegator_addr_2,
1668 &validator_addr_1,
1669 )
1670 .unwrap()
1671 .unwrap();
1672 assert_eq!(Uint256::new(18), rewards.amount);
1673
1674 env.router
1676 .staking
1677 .add_stake(
1678 &env.api,
1679 &mut StakingStorageMut::new(&mut env.storage),
1680 &env.block,
1681 &delegator_addr_1,
1682 &validator_addr_1,
1683 coin(100, BONDED_DENOM),
1684 )
1685 .unwrap();
1686
1687 env.block.time = env.block.time.plus_seconds(YEAR);
1689
1690 let rewards = env
1692 .router
1693 .staking
1694 .get_rewards(
1695 &env.storage,
1696 &env.block,
1697 &delegator_addr_1,
1698 &validator_addr_1,
1699 )
1700 .unwrap()
1701 .unwrap();
1702 assert_eq!(Uint256::new(27), rewards.amount);
1703
1704 let rewards = env
1706 .router
1707 .staking
1708 .get_rewards(
1709 &env.storage,
1710 &env.block,
1711 &delegator_addr_2,
1712 &validator_addr_1,
1713 )
1714 .unwrap()
1715 .unwrap();
1716 assert_eq!(Uint256::new(36), rewards.amount);
1717
1718 env.router
1720 .staking
1721 .remove_stake(
1722 &env.api,
1723 &mut StakingStorageMut::new(&mut env.storage),
1724 &env.block,
1725 &delegator_addr_2,
1726 &validator_addr_1,
1727 coin(100, BONDED_DENOM),
1728 )
1729 .unwrap();
1730
1731 env.router
1733 .distribution
1734 .execute(
1735 &env.api,
1736 &mut env.storage,
1737 &env.router,
1738 &env.block,
1739 delegator_addr_1.clone(),
1740 DistributionMsg::WithdrawDelegatorReward {
1741 validator: validator_addr_1.to_string(),
1742 },
1743 )
1744 .unwrap();
1745
1746 let balance: BalanceResponse = from_json(
1747 env.router
1748 .bank
1749 .query(
1750 &env.api,
1751 &env.storage,
1752 &env.router.querier(&env.api, &env.storage, &env.block),
1753 &env.block,
1754 BankQuery::Balance {
1755 address: delegator_addr_1.to_string(),
1756 denom: BONDED_DENOM.to_string(),
1757 },
1758 )
1759 .unwrap(),
1760 )
1761 .unwrap();
1762 assert_eq!(Uint256::new(27), balance.amount.amount);
1763
1764 let rewards = env
1765 .router
1766 .staking
1767 .get_rewards(
1768 &env.storage,
1769 &env.block,
1770 &delegator_addr_1,
1771 &validator_addr_1,
1772 )
1773 .unwrap()
1774 .unwrap();
1775 assert_eq!(Uint256::zero(), rewards.amount);
1776
1777 env.block.time = env.block.time.plus_seconds(YEAR);
1779
1780 let rewards = env
1782 .router
1783 .staking
1784 .get_rewards(
1785 &env.storage,
1786 &env.block,
1787 &delegator_addr_1,
1788 &validator_addr_1,
1789 )
1790 .unwrap()
1791 .unwrap();
1792 assert_eq!(Uint256::new(18), rewards.amount);
1793
1794 let rewards = env
1796 .router
1797 .staking
1798 .get_rewards(
1799 &env.storage,
1800 &env.block,
1801 &delegator_addr_2,
1802 &validator_addr_1,
1803 )
1804 .unwrap()
1805 .unwrap();
1806 assert_eq!(Uint256::new(45), rewards.amount);
1807 }
1808
1809 #[test]
1810 fn execute() {
1811 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 20, 1));
1812
1813 let validator_addr_1 = env.validator_addr_1();
1814 let validator_addr_2 = env.validator_addr_2();
1815 let delegator_addr_1 = env.delegator_addr_2();
1816 let reward_receiver_addr = env.user_addr_1();
1817
1818 init_balance(&mut env, &delegator_addr_1, 1000);
1820
1821 execute_stake(
1823 &mut env,
1824 delegator_addr_1.clone(),
1825 StakingMsg::Delegate {
1826 validator: validator_addr_1.clone(),
1827 amount: coin(100, BONDED_DENOM),
1828 },
1829 )
1830 .unwrap();
1831
1832 assert_balances(&env, vec![(delegator_addr_1.clone(), 900)]);
1834
1835 env.block.time = env.block.time.plus_seconds(YEAR);
1837
1838 execute_distr(
1840 &mut env,
1841 delegator_addr_1.clone(),
1842 DistributionMsg::SetWithdrawAddress {
1843 address: reward_receiver_addr.to_string(),
1844 },
1845 )
1846 .unwrap();
1847
1848 execute_distr(
1850 &mut env,
1851 delegator_addr_1.clone(),
1852 DistributionMsg::WithdrawDelegatorReward {
1853 validator: validator_addr_1.clone(),
1854 },
1855 )
1856 .unwrap();
1857
1858 assert_balances(
1860 &env,
1861 vec![(reward_receiver_addr, 100 / 10 * 9 / 10)],
1863 );
1864
1865 execute_stake(
1867 &mut env,
1868 delegator_addr_1.clone(),
1869 StakingMsg::Redelegate {
1870 src_validator: validator_addr_1,
1871 dst_validator: validator_addr_2.clone(),
1872 amount: coin(100, BONDED_DENOM),
1873 },
1874 )
1875 .unwrap();
1876
1877 assert_balances(&env, vec![(delegator_addr_1.clone(), 900)]);
1879
1880 let delegations: AllDelegationsResponse = query_stake(
1881 &env,
1882 StakingQuery::AllDelegations {
1883 delegator: delegator_addr_1.to_string(),
1884 },
1885 )
1886 .unwrap();
1887 assert_eq!(
1888 delegations.delegations,
1889 [Delegation::new(
1890 delegator_addr_1.clone(),
1891 validator_addr_2.clone(),
1892 coin(100, BONDED_DENOM),
1893 )]
1894 );
1895
1896 execute_stake(
1898 &mut env,
1899 delegator_addr_1.clone(),
1900 StakingMsg::Undelegate {
1901 validator: validator_addr_2,
1902 amount: coin(100, BONDED_DENOM),
1903 },
1904 )
1905 .unwrap();
1906
1907 env.block.time = env.block.time.plus_seconds(60);
1909
1910 env.router
1912 .staking
1913 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
1914 .unwrap();
1915
1916 assert_balances(&env, vec![(delegator_addr_1.clone(), 1000)]);
1918 }
1919
1920 #[test]
1921 fn can_set_withdraw_address() {
1922 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
1923
1924 let validator_addr_1 = env.validator_addr_1();
1925 let delegator_addr_1 = env.delegator_addr_1();
1926 let reward_receiver_addr = env.user_addr_1();
1927
1928 init_balance(&mut env, &delegator_addr_1, 100);
1930
1931 execute_stake(
1933 &mut env,
1934 delegator_addr_1.clone(),
1935 StakingMsg::Delegate {
1936 validator: validator_addr_1.clone(),
1937 amount: coin(100, BONDED_DENOM),
1938 },
1939 )
1940 .unwrap();
1941
1942 execute_distr(
1944 &mut env,
1945 delegator_addr_1.clone(),
1946 DistributionMsg::SetWithdrawAddress {
1947 address: reward_receiver_addr.to_string(),
1948 },
1949 )
1950 .unwrap();
1951
1952 env.block.time = env.block.time.plus_seconds(YEAR);
1954
1955 execute_distr(
1957 &mut env,
1958 delegator_addr_1.clone(),
1959 DistributionMsg::WithdrawDelegatorReward {
1960 validator: validator_addr_1.clone(),
1961 },
1962 )
1963 .unwrap();
1964
1965 execute_distr(
1967 &mut env,
1968 delegator_addr_1.clone(),
1969 DistributionMsg::SetWithdrawAddress {
1970 address: delegator_addr_1.to_string(),
1971 },
1972 )
1973 .unwrap();
1974
1975 env.block.time = env.block.time.plus_seconds(YEAR);
1977
1978 execute_distr(
1980 &mut env,
1981 delegator_addr_1.clone(),
1982 DistributionMsg::WithdrawDelegatorReward {
1983 validator: validator_addr_1,
1984 },
1985 )
1986 .unwrap();
1987
1988 let rewards_yr = 100 / 10 * 9 / 10;
1990
1991 assert_balances(
1992 &env,
1993 vec![
1994 (reward_receiver_addr, rewards_yr),
1995 (delegator_addr_1, rewards_yr),
1996 ],
1997 );
1998 }
1999
2000 #[test]
2001 fn cannot_steal() {
2002 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 20, 1));
2003
2004 let validator_addr_1 = env.validator_addr_1();
2005 let validator_addr_2 = env.validator_addr_2();
2006 let delegator_addr_1 = env.delegator_addr_1();
2007
2008 init_balance(&mut env, &delegator_addr_1, 100);
2010
2011 execute_stake(
2013 &mut env,
2014 delegator_addr_1.clone(),
2015 StakingMsg::Delegate {
2016 validator: validator_addr_1.clone(),
2017 amount: coin(100, BONDED_DENOM),
2018 },
2019 )
2020 .unwrap();
2021
2022 let error_result = execute_stake(
2024 &mut env,
2025 delegator_addr_1.clone(),
2026 StakingMsg::Undelegate {
2027 validator: validator_addr_1.clone(),
2028 amount: coin(200, BONDED_DENOM),
2029 },
2030 )
2031 .unwrap_err();
2032 assert_eq!(
2033 "kind: Other, error: invalid shares amount",
2034 error_result.to_string()
2035 );
2036
2037 let error_result = execute_stake(
2039 &mut env,
2040 delegator_addr_1.clone(),
2041 StakingMsg::Redelegate {
2042 src_validator: validator_addr_1,
2043 dst_validator: validator_addr_2.clone(),
2044 amount: coin(200, BONDED_DENOM),
2045 },
2046 )
2047 .unwrap_err();
2048 assert_eq!(
2049 "kind: Other, error: invalid shares amount",
2050 error_result.to_string()
2051 );
2052
2053 let error_result = execute_stake(
2055 &mut env,
2056 delegator_addr_1.clone(),
2057 StakingMsg::Undelegate {
2058 validator: validator_addr_2,
2059 amount: coin(100, BONDED_DENOM),
2060 },
2061 )
2062 .unwrap_err();
2063 assert_eq!(
2064 "kind: Other, error: no delegation for (address, validator) tuple",
2065 error_result.to_string()
2066 );
2067 }
2068
2069 #[test]
2070 fn denom_validation() {
2071 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2072
2073 let validator_addr_1 = env.validator_addr_1();
2074 let delegator_addr_1 = env.delegator_addr_1();
2075
2076 init_balance_denom(&mut env, &delegator_addr_1, 100, "FAKE");
2078
2079 let error_result = execute_stake(
2081 &mut env,
2082 delegator_addr_1.clone(),
2083 StakingMsg::Delegate {
2084 validator: validator_addr_1,
2085 amount: coin(100, "FAKE"),
2086 },
2087 )
2088 .unwrap_err();
2089 assert_eq!(
2090 "kind: Other, error: cannot delegate coins of denominator FAKE, only of TOKEN",
2091 error_result.to_string()
2092 );
2093 }
2094
2095 #[test]
2096 fn cannot_slash_nonexistent() {
2097 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2098
2099 let validator_addr_3 = env.validator_addr_3();
2100 let delegator_addr_1 = env.delegator_addr_1();
2101
2102 init_balance_denom(&mut env, &delegator_addr_1, 100, "FAKE");
2104
2105 let error_result = env
2107 .router
2108 .staking
2109 .sudo(
2110 &env.api,
2111 &mut env.storage,
2112 &env.router,
2113 &env.block,
2114 StakingSudo::Slash {
2115 validator: validator_addr_3,
2116 percentage: Decimal256::percent(50),
2117 },
2118 )
2119 .unwrap_err();
2120 assert_eq!(
2121 error_result.to_string(),
2122 "kind: Other, error: validator does not exist"
2123 );
2124 }
2125
2126 #[test]
2127 fn non_existent_validator() {
2128 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2129
2130 let validator_addr_3 = env.validator_addr_3();
2131 let delegator_addr_1 = env.delegator_addr_1();
2132
2133 init_balance(&mut env, &delegator_addr_1, 100);
2135
2136 let error_result = execute_stake(
2138 &mut env,
2139 delegator_addr_1.clone(),
2140 StakingMsg::Delegate {
2141 validator: validator_addr_3.clone(),
2142 amount: coin(100, BONDED_DENOM),
2143 },
2144 )
2145 .unwrap_err();
2146 assert_eq!(
2147 "kind: Other, error: validator does not exist",
2148 error_result.to_string()
2149 );
2150
2151 let error_result = execute_stake(
2153 &mut env,
2154 delegator_addr_1.clone(),
2155 StakingMsg::Undelegate {
2156 validator: validator_addr_3,
2157 amount: coin(100, BONDED_DENOM),
2158 },
2159 )
2160 .unwrap_err();
2161 assert_eq!(
2162 "kind: Other, error: validator does not exist",
2163 error_result.to_string()
2164 );
2165 }
2166
2167 #[test]
2168 fn zero_staking_forbidden() {
2169 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2170
2171 let validator_addr_1 = env.validator_addr_1();
2172 let delegator_addr_1 = env.delegator_addr_1();
2173
2174 let error_result = execute_stake(
2176 &mut env,
2177 delegator_addr_1.clone(),
2178 StakingMsg::Delegate {
2179 validator: validator_addr_1.clone(),
2180 amount: coin(0, BONDED_DENOM),
2181 },
2182 )
2183 .unwrap_err();
2184 assert_eq!(
2185 "kind: Other, error: invalid delegation amount",
2186 error_result.to_string()
2187 );
2188
2189 let error_result = execute_stake(
2191 &mut env,
2192 delegator_addr_1,
2193 StakingMsg::Undelegate {
2194 validator: validator_addr_1,
2195 amount: coin(0, BONDED_DENOM),
2196 },
2197 )
2198 .unwrap_err();
2199 assert_eq!(
2200 "kind: Other, error: invalid shares amount",
2201 error_result.to_string()
2202 );
2203 }
2204
2205 #[test]
2206 fn query_staking() {
2207 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 1, 1));
2208
2209 let validator_addr_1 = env.validator_addr_1();
2210 let validator_addr_2 = env.validator_addr_2();
2211 let delegator_addr_1 = env.delegator_addr_1();
2212 let delegator_addr_2 = env.delegator_addr_2();
2213 let user_addr_1 = env.user_addr_1();
2214
2215 init_balance(&mut env, &delegator_addr_1, 260);
2217 init_balance(&mut env, &delegator_addr_2, 150);
2218
2219 let valoper1: ValidatorResponse = query_stake(
2221 &env,
2222 StakingQuery::Validator {
2223 address: validator_addr_1.to_string(),
2224 },
2225 )
2226 .unwrap();
2227 let valoper2: ValidatorResponse = query_stake(
2228 &env,
2229 StakingQuery::Validator {
2230 address: validator_addr_2.to_string(),
2231 },
2232 )
2233 .unwrap();
2234
2235 let validators: AllValidatorsResponse =
2236 query_stake(&env, StakingQuery::AllValidators {}).unwrap();
2237 assert_eq!(
2238 validators.validators,
2239 [
2240 valoper1.validator.unwrap().into(),
2241 valoper2.validator.unwrap().into()
2242 ]
2243 );
2244
2245 let response = query_stake::<ValidatorResponse>(
2247 &env,
2248 StakingQuery::Validator {
2249 address: user_addr_1.to_string(),
2250 },
2251 )
2252 .unwrap();
2253 assert_eq!(response.validator, None);
2254
2255 let response: BondedDenomResponse =
2257 query_stake(&env, StakingQuery::BondedDenom {}).unwrap();
2258 assert_eq!(response.denom, BONDED_DENOM);
2259
2260 execute_stake(
2262 &mut env,
2263 delegator_addr_1.clone(),
2264 StakingMsg::Delegate {
2265 validator: validator_addr_1.to_string(),
2266 amount: coin(100, BONDED_DENOM),
2267 },
2268 )
2269 .unwrap();
2270 execute_stake(
2271 &mut env,
2272 delegator_addr_1.clone(),
2273 StakingMsg::Delegate {
2274 validator: validator_addr_2.to_string(),
2275 amount: coin(160, BONDED_DENOM),
2276 },
2277 )
2278 .unwrap();
2279 execute_stake(
2280 &mut env,
2281 delegator_addr_2.clone(),
2282 StakingMsg::Delegate {
2283 validator: validator_addr_1.to_string(),
2284 amount: coin(150, BONDED_DENOM),
2285 },
2286 )
2287 .unwrap();
2288 execute_stake(
2290 &mut env,
2291 delegator_addr_1.clone(),
2292 StakingMsg::Undelegate {
2293 validator: validator_addr_1.to_string(),
2294 amount: coin(50, BONDED_DENOM),
2295 },
2296 )
2297 .unwrap();
2298 execute_stake(
2299 &mut env,
2300 delegator_addr_2.clone(),
2301 StakingMsg::Undelegate {
2302 validator: validator_addr_1.to_string(),
2303 amount: coin(50, BONDED_DENOM),
2304 },
2305 )
2306 .unwrap();
2307
2308 let response1: AllDelegationsResponse = query_stake(
2310 &env,
2311 StakingQuery::AllDelegations {
2312 delegator: delegator_addr_1.to_string(),
2313 },
2314 )
2315 .unwrap();
2316 assert_eq!(
2317 response1.delegations,
2318 vec![
2319 Delegation::new(
2320 delegator_addr_1.clone(),
2321 validator_addr_1.to_string(),
2322 coin(50, BONDED_DENOM),
2323 ),
2324 Delegation::new(
2325 delegator_addr_1.clone(),
2326 validator_addr_2,
2327 coin(160, BONDED_DENOM),
2328 ),
2329 ]
2330 );
2331 let response2: DelegationResponse = query_stake(
2332 &env,
2333 StakingQuery::Delegation {
2334 delegator: delegator_addr_2.to_string(),
2335 validator: validator_addr_1.clone(),
2336 },
2337 )
2338 .unwrap();
2339 assert_eq!(
2340 response2.delegation.unwrap(),
2341 FullDelegation::new(
2342 delegator_addr_2.clone(),
2343 validator_addr_1,
2344 coin(100, BONDED_DENOM),
2345 coin(100, BONDED_DENOM),
2346 vec![],
2347 ),
2348 );
2349 }
2350
2351 #[test]
2352 fn delegation_queries_unbonding() {
2353 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2354
2355 let validator_addr_1 = env.validator_addr_1();
2356 let delegator_addr_1 = env.delegator_addr_1();
2357 let delegator_addr_2 = env.delegator_addr_2();
2358
2359 init_balance(&mut env, &delegator_addr_1, 100);
2361 init_balance(&mut env, &delegator_addr_2, 150);
2362
2363 execute_stake(
2365 &mut env,
2366 delegator_addr_1.clone(),
2367 StakingMsg::Delegate {
2368 validator: validator_addr_1.to_string(),
2369 amount: coin(100, BONDED_DENOM),
2370 },
2371 )
2372 .unwrap();
2373 execute_stake(
2374 &mut env,
2375 delegator_addr_2.clone(),
2376 StakingMsg::Delegate {
2377 validator: validator_addr_1.to_string(),
2378 amount: coin(150, BONDED_DENOM),
2379 },
2380 )
2381 .unwrap();
2382 execute_stake(
2384 &mut env,
2385 delegator_addr_1.clone(),
2386 StakingMsg::Undelegate {
2387 validator: validator_addr_1.to_string(),
2388 amount: coin(50, BONDED_DENOM),
2389 },
2390 )
2391 .unwrap();
2392 execute_stake(
2394 &mut env,
2395 delegator_addr_2.clone(),
2396 StakingMsg::Undelegate {
2397 validator: validator_addr_1.to_string(),
2398 amount: coin(150, BONDED_DENOM),
2399 },
2400 )
2401 .unwrap();
2402
2403 let response1: AllDelegationsResponse = query_stake(
2405 &env,
2406 StakingQuery::AllDelegations {
2407 delegator: delegator_addr_1.to_string(),
2408 },
2409 )
2410 .unwrap();
2411 assert_eq!(
2412 response1.delegations,
2413 vec![Delegation::new(
2414 delegator_addr_1.clone(),
2415 validator_addr_1.to_string(),
2416 coin(50, BONDED_DENOM),
2417 )]
2418 );
2419 let response2: DelegationResponse = query_stake(
2420 &env,
2421 StakingQuery::Delegation {
2422 delegator: delegator_addr_2.to_string(),
2423 validator: validator_addr_1.to_string(),
2424 },
2425 )
2426 .unwrap();
2427 assert_eq!(response2.delegation, None);
2428
2429 execute_stake(
2431 &mut env,
2432 delegator_addr_1.clone(),
2433 StakingMsg::Undelegate {
2434 validator: validator_addr_1.to_string(),
2435 amount: coin(25, BONDED_DENOM),
2436 },
2437 )
2438 .unwrap();
2439 env.block.time = env.block.time.plus_seconds(10);
2440 execute_stake(
2441 &mut env,
2442 delegator_addr_1.clone(),
2443 StakingMsg::Undelegate {
2444 validator: validator_addr_1.to_string(),
2445 amount: coin(25, BONDED_DENOM),
2446 },
2447 )
2448 .unwrap();
2449
2450 let response1: DelegationResponse = query_stake(
2452 &env,
2453 StakingQuery::Delegation {
2454 delegator: delegator_addr_1.to_string(),
2455 validator: validator_addr_1,
2456 },
2457 )
2458 .unwrap();
2459 let response2: AllDelegationsResponse = query_stake(
2460 &env,
2461 StakingQuery::AllDelegations {
2462 delegator: delegator_addr_1.to_string(),
2463 },
2464 )
2465 .unwrap();
2466 assert_eq!(
2467 response1.delegation, None,
2468 "delegator1 should have no delegations left"
2469 );
2470 assert_eq!(response2.delegations, vec![]);
2471 }
2472
2473 #[test]
2474 fn partial_unbonding_reduces_stake() {
2475 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2476
2477 let validator_addr_1 = env.validator_addr_1();
2478 let delegator_addr_1 = env.delegator_addr_1();
2479
2480 init_balance(&mut env, &delegator_addr_1, 100);
2482
2483 execute_stake(
2485 &mut env,
2486 delegator_addr_1.clone(),
2487 StakingMsg::Delegate {
2488 validator: validator_addr_1.to_string(),
2489 amount: coin(100, BONDED_DENOM),
2490 },
2491 )
2492 .unwrap();
2493 execute_stake(
2495 &mut env,
2496 delegator_addr_1.clone(),
2497 StakingMsg::Undelegate {
2498 validator: validator_addr_1.to_string(),
2499 amount: coin(50, BONDED_DENOM),
2500 },
2501 )
2502 .unwrap();
2503 env.block.time = env.block.time.plus_seconds(10);
2504 execute_stake(
2505 &mut env,
2506 delegator_addr_1.clone(),
2507 StakingMsg::Undelegate {
2508 validator: validator_addr_1.to_string(),
2509 amount: coin(30, BONDED_DENOM),
2510 },
2511 )
2512 .unwrap();
2513 env.block.time = env.block.time.plus_seconds(10);
2514 execute_stake(
2515 &mut env,
2516 delegator_addr_1.clone(),
2517 StakingMsg::Undelegate {
2518 validator: validator_addr_1.to_string(),
2519 amount: coin(20, BONDED_DENOM),
2520 },
2521 )
2522 .unwrap();
2523
2524 env.block.time = env.block.time.plus_seconds(40);
2526 env.router
2527 .staking
2528 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
2529 .unwrap();
2530
2531 let response1: DelegationResponse = query_stake(
2534 &env,
2535 StakingQuery::Delegation {
2536 delegator: delegator_addr_1.to_string(),
2537 validator: validator_addr_1.to_string(),
2538 },
2539 )
2540 .unwrap();
2541 let response2: AllDelegationsResponse = query_stake(
2542 &env,
2543 StakingQuery::AllDelegations {
2544 delegator: delegator_addr_1.to_string(),
2545 },
2546 )
2547 .unwrap();
2548 assert_eq!(response1.delegation, None);
2549 assert_eq!(response2.delegations, vec![]);
2550
2551 env.block.time = env.block.time.plus_seconds(20);
2553 env.router
2554 .staking
2555 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
2556 .unwrap();
2557
2558 let response1: DelegationResponse = query_stake(
2560 &env,
2561 StakingQuery::Delegation {
2562 delegator: delegator_addr_1.to_string(),
2563 validator: validator_addr_1,
2564 },
2565 )
2566 .unwrap();
2567 let response2: AllDelegationsResponse = query_stake(
2568 &env,
2569 StakingQuery::AllDelegations {
2570 delegator: delegator_addr_1.to_string(),
2571 },
2572 )
2573 .unwrap();
2574 assert_eq!(
2575 response1.delegation, None,
2576 "delegator should have nothing left"
2577 );
2578 assert!(response2.delegations.is_empty());
2579 }
2580
2581 #[test]
2582 fn delegations_slashed() {
2583 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2584
2585 let validator_addr_1 = env.validator_addr_1();
2586 let delegator_addr_1 = env.delegator_addr_1();
2587
2588 init_balance(&mut env, &delegator_addr_1, 333);
2590
2591 execute_stake(
2593 &mut env,
2594 delegator_addr_1.clone(),
2595 StakingMsg::Delegate {
2596 validator: validator_addr_1.to_string(),
2597 amount: coin(333, BONDED_DENOM),
2598 },
2599 )
2600 .unwrap();
2601
2602 execute_stake(
2604 &mut env,
2605 delegator_addr_1.clone(),
2606 StakingMsg::Undelegate {
2607 validator: validator_addr_1.to_string(),
2608 amount: coin(111, BONDED_DENOM),
2609 },
2610 )
2611 .unwrap();
2612
2613 env.router
2615 .staking
2616 .sudo(
2617 &env.api,
2618 &mut env.storage,
2619 &env.router,
2620 &env.block,
2621 StakingSudo::Slash {
2622 validator: validator_addr_1.to_string(),
2623 percentage: Decimal256::percent(50),
2624 },
2625 )
2626 .unwrap();
2627
2628 let response1: AllDelegationsResponse = query_stake(
2630 &env,
2631 StakingQuery::AllDelegations {
2632 delegator: delegator_addr_1.to_string(),
2633 },
2634 )
2635 .unwrap();
2636 assert_eq!(
2637 response1.delegations[0],
2638 Delegation::new(
2639 delegator_addr_1.clone(),
2640 validator_addr_1,
2641 coin(111, BONDED_DENOM),
2642 )
2643 );
2644
2645 env.block.time = env.block.time.plus_seconds(60);
2647 env.router
2648 .staking
2649 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
2650 .unwrap();
2651 let balance =
2652 QuerierWrapper::<Empty>::new(&env.router.querier(&env.api, &env.storage, &env.block))
2653 .query_balance(delegator_addr_1, BONDED_DENOM)
2654 .unwrap();
2655 assert_eq!(Uint256::new(55), balance.amount);
2656 }
2657
2658 #[test]
2659 fn rewards_initial_wait() {
2660 let mut env = TestEnv::new(vp(0, 100, 1), vp(0, 100, 1));
2661
2662 let validator_addr_1 = env.validator_addr_1();
2663 let delegator_addr_1 = env.delegator_addr_1();
2664
2665 init_balance(&mut env, &delegator_addr_1, 100);
2667
2668 env.block.time = env.block.time.plus_seconds(YEAR);
2670
2671 execute_stake(
2673 &mut env,
2674 delegator_addr_1.clone(),
2675 StakingMsg::Delegate {
2676 validator: validator_addr_1.to_string(),
2677 amount: coin(100, BONDED_DENOM),
2678 },
2679 )
2680 .unwrap();
2681
2682 env.block.time = env.block.time.plus_seconds(YEAR);
2684
2685 let response: DelegationResponse = query_stake(
2687 &env,
2688 StakingQuery::Delegation {
2689 delegator: delegator_addr_1.to_string(),
2690 validator: validator_addr_1,
2691 },
2692 )
2693 .unwrap();
2694
2695 assert_eq!(
2696 response.delegation.unwrap().accumulated_rewards,
2697 vec![coin(10, BONDED_DENOM)] );
2699 }
2700}