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