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 {
683 events,
684 ..Default::default()
685 })
686 }
687 StakingMsg::Undelegate { validator, amount } => {
688 self.validate_denom(&staking_storage, &amount)?;
689
690 if amount.amount.is_zero() {
692 bail!("invalid shares amount");
693 }
694
695 let events = vec![Event::new("unbond")
697 .add_attribute("validator", &validator)
698 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))
699 .add_attribute("completion_time", "2022-09-27T14:00:00+00:00")]; self.remove_stake(
701 api,
702 &mut staking_storage,
703 block,
704 &sender,
705 &validator,
706 amount.clone(),
707 )?;
708 let staking_info = Self::get_staking_info(&staking_storage)?;
710 let mut unbonding_queue = UNBONDING_QUEUE
711 .may_load(&staking_storage)?
712 .unwrap_or_default();
713 unbonding_queue.push_back(Unbonding {
714 delegator: sender.clone(),
715 validator,
716 amount: amount.amount,
717 payout_at: block.time.plus_seconds(staking_info.unbonding_time),
718 });
719 UNBONDING_QUEUE.save(&mut staking_storage, &unbonding_queue)?;
720 Ok(AppResponse {
721 events,
722 ..Default::default()
723 })
724 }
725 StakingMsg::Redelegate {
726 src_validator,
727 dst_validator,
728 amount,
729 } => {
730 let events = vec![Event::new("redelegate")
732 .add_attribute("source_validator", &src_validator)
733 .add_attribute("destination_validator", &dst_validator)
734 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))];
735
736 self.remove_stake(
737 api,
738 &mut staking_storage,
739 block,
740 &sender,
741 &src_validator,
742 amount.clone(),
743 )?;
744 self.add_stake(
745 api,
746 &mut staking_storage,
747 block,
748 &sender,
749 &dst_validator,
750 amount,
751 )?;
752
753 Ok(AppResponse {
754 events,
755 ..Default::default()
756 })
757 }
758 m => bail!("Unsupported staking message: {:?}", m),
759 }
760 }
761
762 fn query(
763 &self,
764 api: &dyn Api,
765 storage: &dyn Storage,
766 _querier: &dyn Querier,
767 block: &BlockInfo,
768 request: StakingQuery,
769 ) -> AnyResult<Binary> {
770 let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
771 match request {
772 StakingQuery::BondedDenom {} => Ok(to_json_binary(&BondedDenomResponse::new(
773 Self::get_staking_info(&staking_storage)?.bonded_denom,
774 ))?),
775 StakingQuery::AllDelegations { delegator } => {
776 let delegator = api.addr_validate(&delegator)?;
777 let validators = self.get_validators(&staking_storage)?;
778
779 let res: AnyResult<Vec<Delegation>> =
780 validators
781 .into_iter()
782 .filter_map(|validator| {
783 let delegator = delegator.clone();
784 let amount = self
785 .get_stake(&staking_storage, &delegator, &validator.address)
786 .transpose()?;
787
788 Some(amount.map(|amount| {
789 Delegation::new(delegator, validator.address, amount)
790 }))
791 })
792 .collect();
793
794 Ok(to_json_binary(&AllDelegationsResponse::new(res?))?)
795 }
796 StakingQuery::Delegation {
797 delegator,
798 validator,
799 } => {
800 let validator_obj = match self.get_validator(&staking_storage, &validator)? {
801 Some(validator) => validator,
802 None => bail!("non-existent validator {}", validator),
803 };
804 let delegator = api.addr_validate(&delegator)?;
805
806 let shares = STAKES
807 .may_load(&staking_storage, (&delegator, &validator))?
808 .unwrap_or_default();
809
810 let validator_info = VALIDATOR_INFO.load(&staking_storage, &validator)?;
811 let reward = Self::get_rewards_internal(
812 &staking_storage,
813 block,
814 &shares,
815 &validator_obj,
816 &validator_info,
817 )?;
818 let staking_info = Self::get_staking_info(&staking_storage)?;
819
820 let amount = coin(
821 Uint128::new(1).mul_floor(shares.stake).u128(),
822 staking_info.bonded_denom,
823 );
824
825 let full_delegation_response = if amount.amount.is_zero() {
826 DelegationResponse::new(None)
828 } else {
829 DelegationResponse::new(Some(FullDelegation::new(
830 delegator,
831 validator,
832 amount.clone(),
833 amount, if reward.amount.is_zero() {
835 vec![]
836 } else {
837 vec![reward]
838 },
839 )))
840 };
841
842 let res = to_json_binary(&full_delegation_response)?;
843 Ok(res)
844 }
845 StakingQuery::AllValidators {} => Ok(to_json_binary(&AllValidatorsResponse::new(
846 self.get_validators(&staking_storage)?,
847 ))?),
848 StakingQuery::Validator { address } => Ok(to_json_binary(&ValidatorResponse::new(
849 self.get_validator(&staking_storage, &address)?,
850 ))?),
851 q => bail!("Unsupported staking sudo message: {:?}", q),
852 }
853 }
854
855 fn sudo<ExecC: CustomMsg, QueryC: CustomQuery>(
856 &self,
857 api: &dyn Api,
858 storage: &mut dyn Storage,
859 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
860 block: &BlockInfo,
861 msg: StakingSudo,
862 ) -> AnyResult<AppResponse> {
863 match msg {
864 StakingSudo::Slash {
865 validator,
866 percentage,
867 } => {
868 let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
869 self.validate_percentage(percentage)?;
870 self.slash(api, &mut staking_storage, block, &validator, percentage)?;
871 Ok(AppResponse::default())
872 }
873 }
874 }
875}
876
877#[derive(Default)]
883pub struct DistributionKeeper {}
884
885impl DistributionKeeper {
886 pub fn new() -> Self {
888 Self::default()
889 }
890
891 pub fn remove_rewards(
893 &self,
894 api: &dyn Api,
895 storage: &mut dyn Storage,
896 block: &BlockInfo,
897 delegator: &Addr,
898 validator: &str,
899 ) -> AnyResult<Uint128> {
900 let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
901 StakeKeeper::update_rewards(api, &mut staking_storage, block, validator)?;
903
904 let mut shares = STAKES.load(&staking_storage, (delegator, validator))?;
906 let rewards = Uint128::new(1).mul_floor(shares.rewards); shares.rewards = Decimal::zero();
910 STAKES.save(&mut staking_storage, (delegator, validator), &shares)?;
911
912 Ok(rewards)
913 }
914
915 pub fn get_withdraw_address(storage: &dyn Storage, delegator: &Addr) -> AnyResult<Addr> {
917 Ok(match WITHDRAW_ADDRESS.may_load(storage, delegator)? {
918 Some(a) => a,
919 None => delegator.clone(),
920 })
921 }
922
923 pub fn set_withdraw_address(
927 storage: &mut dyn Storage,
928 delegator: &Addr,
929 withdraw_addr: &Addr,
930 ) -> AnyResult<()> {
931 if delegator == withdraw_addr {
932 WITHDRAW_ADDRESS.remove(storage, delegator);
933 Ok(())
934 } else {
935 WITHDRAW_ADDRESS
938 .save(storage, delegator, withdraw_addr)
939 .map_err(|e| e.into())
940 }
941 }
942}
943
944impl Distribution for DistributionKeeper {}
945
946impl Module for DistributionKeeper {
947 type ExecT = DistributionMsg;
948 type QueryT = Empty;
949 type SudoT = Empty;
950
951 fn execute<ExecC: CustomMsg, QueryC: CustomQuery>(
952 &self,
953 api: &dyn Api,
954 storage: &mut dyn Storage,
955 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
956 block: &BlockInfo,
957 sender: Addr,
958 msg: DistributionMsg,
959 ) -> AnyResult<AppResponse> {
960 match msg {
961 DistributionMsg::WithdrawDelegatorReward { validator } => {
962 let rewards = self.remove_rewards(api, storage, block, &sender, &validator)?;
963 let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
964 let distribution_storage = prefixed_read(storage, NAMESPACE_DISTRIBUTION);
965 let staking_info = StakeKeeper::get_staking_info(&staking_storage)?;
966 let receiver = Self::get_withdraw_address(&distribution_storage, &sender)?;
967 router.sudo(
969 api,
970 storage,
971 block,
972 BankSudo::Mint {
973 to_address: receiver.into_string(),
974 amount: vec![Coin {
975 amount: rewards,
976 denom: staking_info.bonded_denom.clone(),
977 }],
978 }
979 .into(),
980 )?;
981
982 let events = vec![Event::new("withdraw_delegator_reward")
983 .add_attribute("validator", &validator)
984 .add_attribute("sender", &sender)
985 .add_attribute(
986 "amount",
987 format!("{}{}", rewards, staking_info.bonded_denom),
988 )];
989 Ok(AppResponse {
990 events,
991 ..Default::default()
992 })
993 }
994 DistributionMsg::SetWithdrawAddress { address } => {
995 let address = api.addr_validate(&address)?;
996 let storage = &mut prefixed(storage, NAMESPACE_DISTRIBUTION);
998 Self::set_withdraw_address(storage, &sender, &address)?;
999 Ok(AppResponse {
1000 events: vec![Event::new("set_withdraw_address")
1002 .add_attribute("withdraw_address", address)],
1003 ..Default::default()
1004 })
1005 }
1006 m => bail!("Unsupported distribution message: {:?}", m),
1007 }
1008 }
1009
1010 fn query(
1011 &self,
1012 _api: &dyn Api,
1013 _storage: &dyn Storage,
1014 _querier: &dyn Querier,
1015 _block: &BlockInfo,
1016 _request: Empty,
1017 ) -> AnyResult<Binary> {
1018 bail!("Something went wrong - Distribution doesn't have query messages")
1019 }
1020
1021 fn sudo<ExecC, QueryC>(
1022 &self,
1023 _api: &dyn Api,
1024 _storage: &mut dyn Storage,
1025 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
1026 _block: &BlockInfo,
1027 _msg: Empty,
1028 ) -> AnyResult<AppResponse> {
1029 bail!("Something went wrong - Distribution doesn't have sudo messages")
1030 }
1031}
1032
1033#[cfg(test)]
1034mod test {
1035 use super::*;
1036 use crate::{
1037 BankKeeper, FailingModule, GovFailingModule, IbcFailingModule, IntoBech32, Router,
1038 StargateFailing, WasmKeeper,
1039 };
1040 use cosmwasm_std::{
1041 coins, from_json,
1042 testing::{mock_env, MockApi, MockStorage},
1043 BalanceResponse, BankQuery, QuerierWrapper,
1044 };
1045 use serde::de::DeserializeOwned;
1046
1047 struct ValidatorProperties {
1050 commission: Decimal,
1052 max_commission: Decimal,
1054 max_change_rate: Decimal,
1056 }
1057
1058 fn vp(commission: u64, max_commission: u64, max_change_rate: u64) -> ValidatorProperties {
1060 ValidatorProperties {
1061 commission: Decimal::percent(commission),
1062 max_commission: Decimal::percent(max_commission),
1063 max_change_rate: Decimal::percent(max_change_rate),
1064 }
1065 }
1066
1067 type BasicRouter<ExecC = Empty, QueryC = Empty> = Router<
1069 BankKeeper,
1070 FailingModule<ExecC, QueryC, Empty>,
1071 WasmKeeper<ExecC, QueryC>,
1072 StakeKeeper,
1073 DistributionKeeper,
1074 IbcFailingModule,
1075 GovFailingModule,
1076 StargateFailing,
1077 >;
1078
1079 struct TestEnv {
1081 api: MockApi,
1082 storage: MockStorage,
1083 router: BasicRouter,
1084 block: BlockInfo,
1085 validator_addr_1: String,
1086 validator_addr_2: String,
1087 validator_addr_3: String,
1088 delegator_addr_1: Addr,
1089 delegator_addr_2: Addr,
1090 user_addr_1: Addr,
1091 }
1092
1093 impl TestEnv {
1094 fn new(validator1: ValidatorProperties, validator2: ValidatorProperties) -> Self {
1096 fn validator_address(value: &str) -> String {
1099 value.into_bech32_with_prefix("cosmwasmvaloper").to_string()
1100 }
1101
1102 fn user_address(api: &MockApi, value: &str) -> Addr {
1105 api.addr_make(value)
1106 }
1107
1108 let api = MockApi::default();
1109 let router = Router {
1110 wasm: WasmKeeper::new(),
1111 bank: BankKeeper::new(),
1112 custom: FailingModule::new(),
1113 staking: StakeKeeper::new(),
1114 distribution: DistributionKeeper::new(),
1115 ibc: IbcFailingModule::new(),
1116 gov: GovFailingModule::new(),
1117 stargate: StargateFailing,
1118 };
1119 let mut storage = MockStorage::new();
1120 let block = mock_env().block;
1121
1122 let validator_addr_1 = validator_address("validator1");
1123 let validator_addr_2 = validator_address("validator2");
1124 let validator_addr_3 = validator_address("validator3");
1125
1126 router
1128 .staking
1129 .setup(&mut storage, StakingInfo::default())
1130 .unwrap();
1131
1132 let valoper1 = Validator::new(
1134 validator_addr_1.to_string(),
1135 validator1.commission,
1136 validator1.max_commission,
1137 validator1.max_change_rate,
1138 );
1139 router
1140 .staking
1141 .add_validator(&api, &mut storage, &block, valoper1)
1142 .unwrap();
1143
1144 let valoper2 = Validator::new(
1146 validator_addr_2.to_string(),
1147 validator2.commission,
1148 validator2.max_commission,
1149 validator2.max_change_rate,
1150 );
1151 router
1152 .staking
1153 .add_validator(&api, &mut storage, &block, valoper2)
1154 .unwrap();
1155
1156 Self {
1158 api,
1159 storage,
1160 router,
1161 block,
1162 validator_addr_1,
1163 validator_addr_2,
1164 validator_addr_3,
1165 delegator_addr_1: user_address(&api, "delegator1"),
1166 delegator_addr_2: user_address(&api, "delegator2"),
1167 user_addr_1: user_address(&api, "user1"),
1168 }
1169 }
1170
1171 #[inline(always)]
1173 fn validator_addr_1(&self) -> String {
1174 self.validator_addr_1.clone()
1175 }
1176
1177 #[inline(always)]
1179 fn validator_addr_2(&self) -> String {
1180 self.validator_addr_2.clone()
1181 }
1182
1183 #[inline(always)]
1185 fn validator_addr_3(&self) -> String {
1186 self.validator_addr_3.clone()
1187 }
1188
1189 #[inline(always)]
1191 fn delegator_addr_1(&self) -> Addr {
1192 self.delegator_addr_1.clone()
1193 }
1194
1195 #[inline(always)]
1197 fn delegator_addr_2(&self) -> Addr {
1198 self.delegator_addr_2.clone()
1199 }
1200
1201 #[inline(always)]
1203 fn user_addr_1(&self) -> Addr {
1204 self.user_addr_1.clone()
1205 }
1206 }
1207
1208 fn execute_stake(env: &mut TestEnv, sender: Addr, msg: StakingMsg) -> AnyResult<AppResponse> {
1210 env.router.staking.execute(
1211 &env.api,
1212 &mut env.storage,
1213 &env.router,
1214 &env.block,
1215 sender,
1216 msg,
1217 )
1218 }
1219
1220 fn query_stake<T: DeserializeOwned>(env: &TestEnv, msg: StakingQuery) -> AnyResult<T> {
1222 Ok(from_json(env.router.staking.query(
1223 &env.api,
1224 &env.storage,
1225 &env.router.querier(&env.api, &env.storage, &env.block),
1226 &env.block,
1227 msg,
1228 )?)?)
1229 }
1230
1231 fn execute_distr(
1233 env: &mut TestEnv,
1234 sender: Addr,
1235 msg: DistributionMsg,
1236 ) -> AnyResult<AppResponse> {
1237 env.router.distribution.execute(
1238 &env.api,
1239 &mut env.storage,
1240 &env.router,
1241 &env.block,
1242 sender,
1243 msg,
1244 )
1245 }
1246
1247 fn query_bank<T: DeserializeOwned>(env: &TestEnv, msg: BankQuery) -> AnyResult<T> {
1249 Ok(from_json(env.router.bank.query(
1250 &env.api,
1251 &env.storage,
1252 &env.router.querier(&env.api, &env.storage, &env.block),
1253 &env.block,
1254 msg,
1255 )?)?)
1256 }
1257
1258 fn init_balance(env: &mut TestEnv, address: &Addr, amount: u128) {
1260 init_balance_denom(env, address, amount, BONDED_DENOM);
1261 }
1262
1263 fn init_balance_denom(env: &mut TestEnv, address: &Addr, amount: u128, denom: &str) {
1265 env.router
1266 .bank
1267 .init_balance(&mut env.storage, address, coins(amount, denom))
1268 .unwrap();
1269 }
1270
1271 fn assert_balances(env: &TestEnv, balances: impl IntoIterator<Item = (Addr, u128)>) {
1273 for (addr, amount) in balances {
1274 let balance: BalanceResponse = query_bank(
1275 env,
1276 BankQuery::Balance {
1277 address: addr.to_string(),
1278 denom: BONDED_DENOM.to_string(),
1279 },
1280 )
1281 .unwrap();
1282 assert_eq!(balance.amount.amount.u128(), amount);
1283 }
1284 }
1285
1286 #[test]
1287 fn add_get_validators() {
1288 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 20, 1));
1289
1290 let validator_addr_3 = env.validator_addr_3();
1291
1292 let validator = Validator::new(
1294 validator_addr_3.to_string(),
1295 Decimal::percent(1),
1296 Decimal::percent(10),
1297 Decimal::percent(1),
1298 );
1299 env.router
1300 .staking
1301 .add_validator(&env.api, &mut env.storage, &env.block, validator.clone())
1302 .unwrap();
1303
1304 let staking_storage = prefixed_read(&env.storage, NAMESPACE_STAKING);
1306 let val = env
1307 .router
1308 .staking
1309 .get_validator(&staking_storage, &validator_addr_3)
1310 .unwrap()
1311 .unwrap();
1312 assert_eq!(val, validator);
1313
1314 let validator_fake = Validator::new(
1316 validator_addr_3.to_string(),
1317 Decimal::percent(2),
1318 Decimal::percent(20),
1319 Decimal::percent(2),
1320 );
1321 env.router
1322 .staking
1323 .add_validator(&env.api, &mut env.storage, &env.block, validator_fake)
1324 .unwrap_err();
1325
1326 let staking_storage = prefixed_read(&env.storage, NAMESPACE_STAKING);
1328 let val = env
1329 .router
1330 .staking
1331 .get_validator(&staking_storage, &validator_addr_3)
1332 .unwrap()
1333 .unwrap();
1334 assert_eq!(val, validator);
1335 }
1336
1337 #[test]
1338 fn validator_slashing() {
1339 let mut env = TestEnv::new(vp(10, 20, 1), vp(10, 20, 1));
1340
1341 let validator_addr_1 = env.validator_addr_1();
1342 let delegator_addr_1 = env.delegator_addr_1();
1343
1344 let mut staking_storage = prefixed(&mut env.storage, NAMESPACE_STAKING);
1346 env.router
1347 .staking
1348 .add_stake(
1349 &env.api,
1350 &mut staking_storage,
1351 &env.block,
1352 &delegator_addr_1,
1353 &validator_addr_1,
1354 coin(100, BONDED_DENOM),
1355 )
1356 .unwrap();
1357
1358 env.router
1360 .staking
1361 .sudo(
1362 &env.api,
1363 &mut env.storage,
1364 &env.router,
1365 &env.block,
1366 StakingSudo::Slash {
1367 validator: validator_addr_1.to_string(),
1368 percentage: Decimal::percent(50),
1369 },
1370 )
1371 .unwrap();
1372
1373 let staking_storage = prefixed(&mut env.storage, NAMESPACE_STAKING);
1375 let stake_left = env
1376 .router
1377 .staking
1378 .get_stake(&staking_storage, &delegator_addr_1, &validator_addr_1)
1379 .unwrap()
1380 .unwrap();
1381 assert_eq!(50, stake_left.amount.u128());
1382
1383 env.router
1385 .staking
1386 .sudo(
1387 &env.api,
1388 &mut env.storage,
1389 &env.router,
1390 &env.block,
1391 StakingSudo::Slash {
1392 validator: validator_addr_1.to_string(),
1393 percentage: Decimal::percent(100),
1394 },
1395 )
1396 .unwrap();
1397
1398 let staking_storage = prefixed(&mut env.storage, NAMESPACE_STAKING);
1400 let stake_left = env
1401 .router
1402 .staking
1403 .get_stake(&staking_storage, &delegator_addr_1, &validator_addr_1)
1404 .unwrap();
1405 assert_eq!(None, stake_left);
1406 }
1407
1408 #[test]
1409 fn rewards_work_for_single_delegator() {
1410 let mut env = TestEnv::new(vp(10, 20, 1), vp(10, 20, 1));
1411
1412 let validator_addr_1 = env.validator_addr_1();
1413 let delegator_addr_1 = env.delegator_addr_1();
1414
1415 let mut staking_storage = prefixed(&mut env.storage, NAMESPACE_STAKING);
1416 env.router
1418 .staking
1419 .add_stake(
1420 &env.api,
1421 &mut staking_storage,
1422 &env.block,
1423 &delegator_addr_1,
1424 &validator_addr_1,
1425 coin(200, BONDED_DENOM),
1426 )
1427 .unwrap();
1428
1429 env.block.time = env.block.time.plus_seconds(YEAR / 2);
1431
1432 let rewards = env
1434 .router
1435 .staking
1436 .get_rewards(
1437 &env.storage,
1438 &env.block,
1439 &delegator_addr_1,
1440 &validator_addr_1,
1441 )
1442 .unwrap()
1443 .unwrap();
1444 assert_eq!(9, rewards.amount.u128());
1445
1446 env.router
1448 .distribution
1449 .execute(
1450 &env.api,
1451 &mut env.storage,
1452 &env.router,
1453 &env.block,
1454 delegator_addr_1.clone(),
1455 DistributionMsg::WithdrawDelegatorReward {
1456 validator: validator_addr_1.to_string(),
1457 },
1458 )
1459 .unwrap();
1460
1461 let rewards = env
1463 .router
1464 .staking
1465 .get_rewards(
1466 &env.storage,
1467 &env.block,
1468 &delegator_addr_1,
1469 &validator_addr_1,
1470 )
1471 .unwrap()
1472 .unwrap();
1473 assert_eq!(0, rewards.amount.u128());
1474
1475 env.block.time = env.block.time.plus_seconds(YEAR / 2);
1477 let rewards = env
1479 .router
1480 .staking
1481 .get_rewards(
1482 &env.storage,
1483 &env.block,
1484 &delegator_addr_1,
1485 &validator_addr_1,
1486 )
1487 .unwrap()
1488 .unwrap();
1489 assert_eq!(9, rewards.amount.u128());
1490 }
1491
1492 #[test]
1493 fn rewards_work_for_multiple_delegators() {
1494 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
1495
1496 let validator_addr_1 = env.validator_addr_1();
1497 let delegator_addr_1 = env.delegator_addr_1();
1498 let delegator_addr_2 = env.delegator_addr_2();
1499
1500 let mut staking_storage = prefixed(&mut env.storage, NAMESPACE_STAKING);
1501
1502 env.router
1504 .staking
1505 .add_stake(
1506 &env.api,
1507 &mut staking_storage,
1508 &env.block,
1509 &delegator_addr_1,
1510 &validator_addr_1,
1511 coin(100, BONDED_DENOM),
1512 )
1513 .unwrap();
1514 env.router
1515 .staking
1516 .add_stake(
1517 &env.api,
1518 &mut staking_storage,
1519 &env.block,
1520 &delegator_addr_2,
1521 &validator_addr_1,
1522 coin(200, BONDED_DENOM),
1523 )
1524 .unwrap();
1525
1526 env.block.time = env.block.time.plus_seconds(YEAR);
1528
1529 let rewards = env
1531 .router
1532 .staking
1533 .get_rewards(
1534 &env.storage,
1535 &env.block,
1536 &delegator_addr_1,
1537 &validator_addr_1,
1538 )
1539 .unwrap()
1540 .unwrap();
1541 assert_eq!(rewards.amount.u128(), 9);
1542
1543 let rewards = env
1545 .router
1546 .staking
1547 .get_rewards(
1548 &env.storage,
1549 &env.block,
1550 &delegator_addr_2,
1551 &validator_addr_1,
1552 )
1553 .unwrap()
1554 .unwrap();
1555 assert_eq!(rewards.amount.u128(), 18);
1556
1557 let mut staking_storage = prefixed(&mut env.storage, NAMESPACE_STAKING);
1559 env.router
1560 .staking
1561 .add_stake(
1562 &env.api,
1563 &mut staking_storage,
1564 &env.block,
1565 &delegator_addr_1,
1566 &validator_addr_1,
1567 coin(100, BONDED_DENOM),
1568 )
1569 .unwrap();
1570
1571 env.block.time = env.block.time.plus_seconds(YEAR);
1573
1574 let rewards = env
1576 .router
1577 .staking
1578 .get_rewards(
1579 &env.storage,
1580 &env.block,
1581 &delegator_addr_1,
1582 &validator_addr_1,
1583 )
1584 .unwrap()
1585 .unwrap();
1586 assert_eq!(rewards.amount.u128(), 27);
1587
1588 let rewards = env
1590 .router
1591 .staking
1592 .get_rewards(
1593 &env.storage,
1594 &env.block,
1595 &delegator_addr_2,
1596 &validator_addr_1,
1597 )
1598 .unwrap()
1599 .unwrap();
1600 assert_eq!(rewards.amount.u128(), 36);
1601
1602 let mut staking_storage = prefixed(&mut env.storage, NAMESPACE_STAKING);
1604 env.router
1605 .staking
1606 .remove_stake(
1607 &env.api,
1608 &mut staking_storage,
1609 &env.block,
1610 &delegator_addr_2,
1611 &validator_addr_1,
1612 coin(100, BONDED_DENOM),
1613 )
1614 .unwrap();
1615
1616 env.router
1618 .distribution
1619 .execute(
1620 &env.api,
1621 &mut env.storage,
1622 &env.router,
1623 &env.block,
1624 delegator_addr_1.clone(),
1625 DistributionMsg::WithdrawDelegatorReward {
1626 validator: validator_addr_1.to_string(),
1627 },
1628 )
1629 .unwrap();
1630
1631 let balance: BalanceResponse = from_json(
1632 env.router
1633 .bank
1634 .query(
1635 &env.api,
1636 &env.storage,
1637 &env.router.querier(&env.api, &env.storage, &env.block),
1638 &env.block,
1639 BankQuery::Balance {
1640 address: delegator_addr_1.to_string(),
1641 denom: BONDED_DENOM.to_string(),
1642 },
1643 )
1644 .unwrap(),
1645 )
1646 .unwrap();
1647 assert_eq!(27, balance.amount.amount.u128());
1648
1649 let rewards = env
1650 .router
1651 .staking
1652 .get_rewards(
1653 &env.storage,
1654 &env.block,
1655 &delegator_addr_1,
1656 &validator_addr_1,
1657 )
1658 .unwrap()
1659 .unwrap();
1660 assert_eq!(0, rewards.amount.u128());
1661
1662 env.block.time = env.block.time.plus_seconds(YEAR);
1664
1665 let rewards = env
1667 .router
1668 .staking
1669 .get_rewards(
1670 &env.storage,
1671 &env.block,
1672 &delegator_addr_1,
1673 &validator_addr_1,
1674 )
1675 .unwrap()
1676 .unwrap();
1677 assert_eq!(18, rewards.amount.u128());
1678
1679 let rewards = env
1681 .router
1682 .staking
1683 .get_rewards(
1684 &env.storage,
1685 &env.block,
1686 &delegator_addr_2,
1687 &validator_addr_1,
1688 )
1689 .unwrap()
1690 .unwrap();
1691 assert_eq!(45, rewards.amount.u128());
1692 }
1693
1694 #[test]
1695 fn execute() {
1696 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 20, 1));
1697
1698 let validator_addr_1 = env.validator_addr_1();
1699 let validator_addr_2 = env.validator_addr_2();
1700 let delegator_addr_1 = env.delegator_addr_2();
1701 let reward_receiver_addr = env.user_addr_1();
1702
1703 init_balance(&mut env, &delegator_addr_1, 1000);
1705
1706 execute_stake(
1708 &mut env,
1709 delegator_addr_1.clone(),
1710 StakingMsg::Delegate {
1711 validator: validator_addr_1.clone(),
1712 amount: coin(100, BONDED_DENOM),
1713 },
1714 )
1715 .unwrap();
1716
1717 assert_balances(&env, vec![(delegator_addr_1.clone(), 900)]);
1719
1720 env.block.time = env.block.time.plus_seconds(YEAR);
1722
1723 execute_distr(
1725 &mut env,
1726 delegator_addr_1.clone(),
1727 DistributionMsg::SetWithdrawAddress {
1728 address: reward_receiver_addr.to_string(),
1729 },
1730 )
1731 .unwrap();
1732
1733 execute_distr(
1735 &mut env,
1736 delegator_addr_1.clone(),
1737 DistributionMsg::WithdrawDelegatorReward {
1738 validator: validator_addr_1.clone(),
1739 },
1740 )
1741 .unwrap();
1742
1743 assert_balances(
1745 &env,
1746 vec![(reward_receiver_addr, 100 / 10 * 9 / 10)],
1748 );
1749
1750 execute_stake(
1752 &mut env,
1753 delegator_addr_1.clone(),
1754 StakingMsg::Redelegate {
1755 src_validator: validator_addr_1,
1756 dst_validator: validator_addr_2.clone(),
1757 amount: coin(100, BONDED_DENOM),
1758 },
1759 )
1760 .unwrap();
1761
1762 assert_balances(&env, vec![(delegator_addr_1.clone(), 900)]);
1764
1765 let delegations: AllDelegationsResponse = query_stake(
1766 &env,
1767 StakingQuery::AllDelegations {
1768 delegator: delegator_addr_1.to_string(),
1769 },
1770 )
1771 .unwrap();
1772 assert_eq!(
1773 delegations.delegations,
1774 [Delegation::new(
1775 delegator_addr_1.clone(),
1776 validator_addr_2.clone(),
1777 coin(100, BONDED_DENOM),
1778 )]
1779 );
1780
1781 execute_stake(
1783 &mut env,
1784 delegator_addr_1.clone(),
1785 StakingMsg::Undelegate {
1786 validator: validator_addr_2,
1787 amount: coin(100, BONDED_DENOM),
1788 },
1789 )
1790 .unwrap();
1791
1792 env.block.time = env.block.time.plus_seconds(60);
1794
1795 env.router
1797 .staking
1798 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
1799 .unwrap();
1800
1801 assert_balances(&env, vec![(delegator_addr_1.clone(), 1000)]);
1803 }
1804
1805 #[test]
1806 fn can_set_withdraw_address() {
1807 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
1808
1809 let validator_addr_1 = env.validator_addr_1();
1810 let delegator_addr_1 = env.delegator_addr_1();
1811 let reward_receiver_addr = env.user_addr_1();
1812
1813 init_balance(&mut env, &delegator_addr_1, 100);
1815
1816 execute_stake(
1818 &mut env,
1819 delegator_addr_1.clone(),
1820 StakingMsg::Delegate {
1821 validator: validator_addr_1.clone(),
1822 amount: coin(100, BONDED_DENOM),
1823 },
1824 )
1825 .unwrap();
1826
1827 execute_distr(
1829 &mut env,
1830 delegator_addr_1.clone(),
1831 DistributionMsg::SetWithdrawAddress {
1832 address: reward_receiver_addr.to_string(),
1833 },
1834 )
1835 .unwrap();
1836
1837 env.block.time = env.block.time.plus_seconds(YEAR);
1839
1840 execute_distr(
1842 &mut env,
1843 delegator_addr_1.clone(),
1844 DistributionMsg::WithdrawDelegatorReward {
1845 validator: validator_addr_1.clone(),
1846 },
1847 )
1848 .unwrap();
1849
1850 execute_distr(
1852 &mut env,
1853 delegator_addr_1.clone(),
1854 DistributionMsg::SetWithdrawAddress {
1855 address: delegator_addr_1.to_string(),
1856 },
1857 )
1858 .unwrap();
1859
1860 env.block.time = env.block.time.plus_seconds(YEAR);
1862
1863 execute_distr(
1865 &mut env,
1866 delegator_addr_1.clone(),
1867 DistributionMsg::WithdrawDelegatorReward {
1868 validator: validator_addr_1,
1869 },
1870 )
1871 .unwrap();
1872
1873 let rewards_yr = 100 / 10 * 9 / 10;
1875
1876 assert_balances(
1877 &env,
1878 vec![
1879 (reward_receiver_addr, rewards_yr),
1880 (delegator_addr_1, rewards_yr),
1881 ],
1882 );
1883 }
1884
1885 #[test]
1886 fn cannot_steal() {
1887 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 20, 1));
1888
1889 let validator_addr_1 = env.validator_addr_1();
1890 let validator_addr_2 = env.validator_addr_2();
1891 let delegator_addr_1 = env.delegator_addr_1();
1892
1893 init_balance(&mut env, &delegator_addr_1, 100);
1895
1896 execute_stake(
1898 &mut env,
1899 delegator_addr_1.clone(),
1900 StakingMsg::Delegate {
1901 validator: validator_addr_1.clone(),
1902 amount: coin(100, BONDED_DENOM),
1903 },
1904 )
1905 .unwrap();
1906
1907 let error_result = execute_stake(
1909 &mut env,
1910 delegator_addr_1.clone(),
1911 StakingMsg::Undelegate {
1912 validator: validator_addr_1.clone(),
1913 amount: coin(200, BONDED_DENOM),
1914 },
1915 )
1916 .unwrap_err();
1917 assert_eq!(error_result.to_string(), "invalid shares amount");
1918
1919 let error_result = execute_stake(
1921 &mut env,
1922 delegator_addr_1.clone(),
1923 StakingMsg::Redelegate {
1924 src_validator: validator_addr_1,
1925 dst_validator: validator_addr_2.clone(),
1926 amount: coin(200, BONDED_DENOM),
1927 },
1928 )
1929 .unwrap_err();
1930 assert_eq!(error_result.to_string(), "invalid shares amount");
1931
1932 let error_result = execute_stake(
1934 &mut env,
1935 delegator_addr_1.clone(),
1936 StakingMsg::Undelegate {
1937 validator: validator_addr_2,
1938 amount: coin(100, BONDED_DENOM),
1939 },
1940 )
1941 .unwrap_err();
1942 assert_eq!(
1943 error_result.to_string(),
1944 "no delegation for (address, validator) tuple"
1945 );
1946 }
1947
1948 #[test]
1949 fn denom_validation() {
1950 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
1951
1952 let validator_addr_1 = env.validator_addr_1();
1953 let delegator_addr_1 = env.delegator_addr_1();
1954
1955 init_balance_denom(&mut env, &delegator_addr_1, 100, "FAKE");
1957
1958 let error_result = execute_stake(
1960 &mut env,
1961 delegator_addr_1.clone(),
1962 StakingMsg::Delegate {
1963 validator: validator_addr_1,
1964 amount: coin(100, "FAKE"),
1965 },
1966 )
1967 .unwrap_err();
1968 assert_eq!(
1969 error_result.to_string(),
1970 "cannot delegate coins of denominator FAKE, only of TOKEN",
1971 );
1972 }
1973
1974 #[test]
1975 fn cannot_slash_nonexistent() {
1976 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
1977
1978 let validator_addr_3 = env.validator_addr_3();
1979 let delegator_addr_1 = env.delegator_addr_1();
1980
1981 init_balance_denom(&mut env, &delegator_addr_1, 100, "FAKE");
1983
1984 let error_result = env
1986 .router
1987 .staking
1988 .sudo(
1989 &env.api,
1990 &mut env.storage,
1991 &env.router,
1992 &env.block,
1993 StakingSudo::Slash {
1994 validator: validator_addr_3,
1995 percentage: Decimal::percent(50),
1996 },
1997 )
1998 .unwrap_err();
1999 assert_eq!(error_result.to_string(), "validator does not exist");
2000 }
2001
2002 #[test]
2003 fn non_existent_validator() {
2004 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2005
2006 let validator_addr_3 = env.validator_addr_3();
2007 let delegator_addr_1 = env.delegator_addr_1();
2008
2009 init_balance(&mut env, &delegator_addr_1, 100);
2011
2012 let error_result = execute_stake(
2014 &mut env,
2015 delegator_addr_1.clone(),
2016 StakingMsg::Delegate {
2017 validator: validator_addr_3.clone(),
2018 amount: coin(100, BONDED_DENOM),
2019 },
2020 )
2021 .unwrap_err();
2022 assert_eq!(error_result.to_string(), "validator does not exist");
2023
2024 let error_result = execute_stake(
2026 &mut env,
2027 delegator_addr_1.clone(),
2028 StakingMsg::Undelegate {
2029 validator: validator_addr_3,
2030 amount: coin(100, BONDED_DENOM),
2031 },
2032 )
2033 .unwrap_err();
2034 assert_eq!(error_result.to_string(), "validator does not exist");
2035 }
2036
2037 #[test]
2038 fn zero_staking_forbidden() {
2039 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2040
2041 let validator_addr_1 = env.validator_addr_1();
2042 let delegator_addr_1 = env.delegator_addr_1();
2043
2044 let error_result = execute_stake(
2046 &mut env,
2047 delegator_addr_1.clone(),
2048 StakingMsg::Delegate {
2049 validator: validator_addr_1.clone(),
2050 amount: coin(0, BONDED_DENOM),
2051 },
2052 )
2053 .unwrap_err();
2054 assert_eq!(error_result.to_string(), "invalid delegation amount");
2055
2056 let error_result = execute_stake(
2058 &mut env,
2059 delegator_addr_1,
2060 StakingMsg::Undelegate {
2061 validator: validator_addr_1,
2062 amount: coin(0, BONDED_DENOM),
2063 },
2064 )
2065 .unwrap_err();
2066 assert_eq!(error_result.to_string(), "invalid shares amount");
2067 }
2068
2069 #[test]
2070 fn query_staking() {
2071 let mut env = TestEnv::new(vp(10, 100, 1), vp(0, 1, 1));
2072
2073 let validator_addr_1 = env.validator_addr_1();
2074 let validator_addr_2 = env.validator_addr_2();
2075 let delegator_addr_1 = env.delegator_addr_1();
2076 let delegator_addr_2 = env.delegator_addr_2();
2077 let user_addr_1 = env.user_addr_1();
2078
2079 init_balance(&mut env, &delegator_addr_1, 260);
2081 init_balance(&mut env, &delegator_addr_2, 150);
2082
2083 let valoper1: ValidatorResponse = query_stake(
2085 &env,
2086 StakingQuery::Validator {
2087 address: validator_addr_1.to_string(),
2088 },
2089 )
2090 .unwrap();
2091 let valoper2: ValidatorResponse = query_stake(
2092 &env,
2093 StakingQuery::Validator {
2094 address: validator_addr_2.to_string(),
2095 },
2096 )
2097 .unwrap();
2098
2099 let validators: AllValidatorsResponse =
2100 query_stake(&env, StakingQuery::AllValidators {}).unwrap();
2101 assert_eq!(
2102 validators.validators,
2103 [valoper1.validator.unwrap(), valoper2.validator.unwrap()]
2104 );
2105
2106 let response = query_stake::<ValidatorResponse>(
2108 &env,
2109 StakingQuery::Validator {
2110 address: user_addr_1.to_string(),
2111 },
2112 )
2113 .unwrap();
2114 assert_eq!(response.validator, None);
2115
2116 let response: BondedDenomResponse =
2118 query_stake(&env, StakingQuery::BondedDenom {}).unwrap();
2119 assert_eq!(response.denom, BONDED_DENOM);
2120
2121 execute_stake(
2123 &mut env,
2124 delegator_addr_1.clone(),
2125 StakingMsg::Delegate {
2126 validator: validator_addr_1.to_string(),
2127 amount: coin(100, BONDED_DENOM),
2128 },
2129 )
2130 .unwrap();
2131 execute_stake(
2132 &mut env,
2133 delegator_addr_1.clone(),
2134 StakingMsg::Delegate {
2135 validator: validator_addr_2.to_string(),
2136 amount: coin(160, BONDED_DENOM),
2137 },
2138 )
2139 .unwrap();
2140 execute_stake(
2141 &mut env,
2142 delegator_addr_2.clone(),
2143 StakingMsg::Delegate {
2144 validator: validator_addr_1.to_string(),
2145 amount: coin(150, BONDED_DENOM),
2146 },
2147 )
2148 .unwrap();
2149 execute_stake(
2151 &mut env,
2152 delegator_addr_1.clone(),
2153 StakingMsg::Undelegate {
2154 validator: validator_addr_1.to_string(),
2155 amount: coin(50, BONDED_DENOM),
2156 },
2157 )
2158 .unwrap();
2159 execute_stake(
2160 &mut env,
2161 delegator_addr_2.clone(),
2162 StakingMsg::Undelegate {
2163 validator: validator_addr_1.to_string(),
2164 amount: coin(50, BONDED_DENOM),
2165 },
2166 )
2167 .unwrap();
2168
2169 let response1: AllDelegationsResponse = query_stake(
2171 &env,
2172 StakingQuery::AllDelegations {
2173 delegator: delegator_addr_1.to_string(),
2174 },
2175 )
2176 .unwrap();
2177 assert_eq!(
2178 response1.delegations,
2179 vec![
2180 Delegation::new(
2181 delegator_addr_1.clone(),
2182 validator_addr_1.to_string(),
2183 coin(50, BONDED_DENOM),
2184 ),
2185 Delegation::new(
2186 delegator_addr_1.clone(),
2187 validator_addr_2,
2188 coin(160, BONDED_DENOM),
2189 ),
2190 ]
2191 );
2192 let response2: DelegationResponse = query_stake(
2193 &env,
2194 StakingQuery::Delegation {
2195 delegator: delegator_addr_2.to_string(),
2196 validator: validator_addr_1.clone(),
2197 },
2198 )
2199 .unwrap();
2200 assert_eq!(
2201 response2.delegation.unwrap(),
2202 FullDelegation::new(
2203 delegator_addr_2.clone(),
2204 validator_addr_1,
2205 coin(100, BONDED_DENOM),
2206 coin(100, BONDED_DENOM),
2207 vec![],
2208 ),
2209 );
2210 }
2211
2212 #[test]
2213 fn delegation_queries_unbonding() {
2214 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2215
2216 let validator_addr_1 = env.validator_addr_1();
2217 let delegator_addr_1 = env.delegator_addr_1();
2218 let delegator_addr_2 = env.delegator_addr_2();
2219
2220 init_balance(&mut env, &delegator_addr_1, 100);
2222 init_balance(&mut env, &delegator_addr_2, 150);
2223
2224 execute_stake(
2226 &mut env,
2227 delegator_addr_1.clone(),
2228 StakingMsg::Delegate {
2229 validator: validator_addr_1.to_string(),
2230 amount: coin(100, BONDED_DENOM),
2231 },
2232 )
2233 .unwrap();
2234 execute_stake(
2235 &mut env,
2236 delegator_addr_2.clone(),
2237 StakingMsg::Delegate {
2238 validator: validator_addr_1.to_string(),
2239 amount: coin(150, BONDED_DENOM),
2240 },
2241 )
2242 .unwrap();
2243 execute_stake(
2245 &mut env,
2246 delegator_addr_1.clone(),
2247 StakingMsg::Undelegate {
2248 validator: validator_addr_1.to_string(),
2249 amount: coin(50, BONDED_DENOM),
2250 },
2251 )
2252 .unwrap();
2253 execute_stake(
2255 &mut env,
2256 delegator_addr_2.clone(),
2257 StakingMsg::Undelegate {
2258 validator: validator_addr_1.to_string(),
2259 amount: coin(150, BONDED_DENOM),
2260 },
2261 )
2262 .unwrap();
2263
2264 let response1: AllDelegationsResponse = query_stake(
2266 &env,
2267 StakingQuery::AllDelegations {
2268 delegator: delegator_addr_1.to_string(),
2269 },
2270 )
2271 .unwrap();
2272 assert_eq!(
2273 response1.delegations,
2274 vec![Delegation::new(
2275 delegator_addr_1.clone(),
2276 validator_addr_1.to_string(),
2277 coin(50, BONDED_DENOM),
2278 )]
2279 );
2280 let response2: DelegationResponse = query_stake(
2281 &env,
2282 StakingQuery::Delegation {
2283 delegator: delegator_addr_2.to_string(),
2284 validator: validator_addr_1.to_string(),
2285 },
2286 )
2287 .unwrap();
2288 assert_eq!(response2.delegation, None);
2289
2290 execute_stake(
2292 &mut env,
2293 delegator_addr_1.clone(),
2294 StakingMsg::Undelegate {
2295 validator: validator_addr_1.to_string(),
2296 amount: coin(25, BONDED_DENOM),
2297 },
2298 )
2299 .unwrap();
2300 env.block.time = env.block.time.plus_seconds(10);
2301 execute_stake(
2302 &mut env,
2303 delegator_addr_1.clone(),
2304 StakingMsg::Undelegate {
2305 validator: validator_addr_1.to_string(),
2306 amount: coin(25, BONDED_DENOM),
2307 },
2308 )
2309 .unwrap();
2310
2311 let response1: DelegationResponse = query_stake(
2313 &env,
2314 StakingQuery::Delegation {
2315 delegator: delegator_addr_1.to_string(),
2316 validator: validator_addr_1,
2317 },
2318 )
2319 .unwrap();
2320 let response2: AllDelegationsResponse = query_stake(
2321 &env,
2322 StakingQuery::AllDelegations {
2323 delegator: delegator_addr_1.to_string(),
2324 },
2325 )
2326 .unwrap();
2327 assert_eq!(
2328 response1.delegation, None,
2329 "delegator1 should have no delegations left"
2330 );
2331 assert_eq!(response2.delegations, vec![]);
2332 }
2333
2334 #[test]
2335 fn partial_unbonding_reduces_stake() {
2336 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2337
2338 let validator_addr_1 = env.validator_addr_1();
2339 let delegator_addr_1 = env.delegator_addr_1();
2340
2341 init_balance(&mut env, &delegator_addr_1, 100);
2343
2344 execute_stake(
2346 &mut env,
2347 delegator_addr_1.clone(),
2348 StakingMsg::Delegate {
2349 validator: validator_addr_1.to_string(),
2350 amount: coin(100, BONDED_DENOM),
2351 },
2352 )
2353 .unwrap();
2354 execute_stake(
2356 &mut env,
2357 delegator_addr_1.clone(),
2358 StakingMsg::Undelegate {
2359 validator: validator_addr_1.to_string(),
2360 amount: coin(50, BONDED_DENOM),
2361 },
2362 )
2363 .unwrap();
2364 env.block.time = env.block.time.plus_seconds(10);
2365 execute_stake(
2366 &mut env,
2367 delegator_addr_1.clone(),
2368 StakingMsg::Undelegate {
2369 validator: validator_addr_1.to_string(),
2370 amount: coin(30, BONDED_DENOM),
2371 },
2372 )
2373 .unwrap();
2374 env.block.time = env.block.time.plus_seconds(10);
2375 execute_stake(
2376 &mut env,
2377 delegator_addr_1.clone(),
2378 StakingMsg::Undelegate {
2379 validator: validator_addr_1.to_string(),
2380 amount: coin(20, BONDED_DENOM),
2381 },
2382 )
2383 .unwrap();
2384
2385 env.block.time = env.block.time.plus_seconds(40);
2387 env.router
2388 .staking
2389 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
2390 .unwrap();
2391
2392 let response1: DelegationResponse = query_stake(
2395 &env,
2396 StakingQuery::Delegation {
2397 delegator: delegator_addr_1.to_string(),
2398 validator: validator_addr_1.to_string(),
2399 },
2400 )
2401 .unwrap();
2402 let response2: AllDelegationsResponse = query_stake(
2403 &env,
2404 StakingQuery::AllDelegations {
2405 delegator: delegator_addr_1.to_string(),
2406 },
2407 )
2408 .unwrap();
2409 assert_eq!(response1.delegation, None);
2410 assert_eq!(response2.delegations, vec![]);
2411
2412 env.block.time = env.block.time.plus_seconds(20);
2414 env.router
2415 .staking
2416 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
2417 .unwrap();
2418
2419 let response1: DelegationResponse = query_stake(
2421 &env,
2422 StakingQuery::Delegation {
2423 delegator: delegator_addr_1.to_string(),
2424 validator: validator_addr_1,
2425 },
2426 )
2427 .unwrap();
2428 let response2: AllDelegationsResponse = query_stake(
2429 &env,
2430 StakingQuery::AllDelegations {
2431 delegator: delegator_addr_1.to_string(),
2432 },
2433 )
2434 .unwrap();
2435 assert_eq!(
2436 response1.delegation, None,
2437 "delegator should have nothing left"
2438 );
2439 assert!(response2.delegations.is_empty());
2440 }
2441
2442 #[test]
2443 fn delegations_slashed() {
2444 let mut env = TestEnv::new(vp(10, 100, 1), vp(10, 100, 1));
2445
2446 let validator_addr_1 = env.validator_addr_1();
2447 let delegator_addr_1 = env.delegator_addr_1();
2448
2449 init_balance(&mut env, &delegator_addr_1, 333);
2451
2452 execute_stake(
2454 &mut env,
2455 delegator_addr_1.clone(),
2456 StakingMsg::Delegate {
2457 validator: validator_addr_1.to_string(),
2458 amount: coin(333, BONDED_DENOM),
2459 },
2460 )
2461 .unwrap();
2462
2463 execute_stake(
2465 &mut env,
2466 delegator_addr_1.clone(),
2467 StakingMsg::Undelegate {
2468 validator: validator_addr_1.to_string(),
2469 amount: coin(111, BONDED_DENOM),
2470 },
2471 )
2472 .unwrap();
2473
2474 env.router
2476 .staking
2477 .sudo(
2478 &env.api,
2479 &mut env.storage,
2480 &env.router,
2481 &env.block,
2482 StakingSudo::Slash {
2483 validator: validator_addr_1.to_string(),
2484 percentage: Decimal::percent(50),
2485 },
2486 )
2487 .unwrap();
2488
2489 let response1: AllDelegationsResponse = query_stake(
2491 &env,
2492 StakingQuery::AllDelegations {
2493 delegator: delegator_addr_1.to_string(),
2494 },
2495 )
2496 .unwrap();
2497 assert_eq!(
2498 response1.delegations[0],
2499 Delegation::new(
2500 delegator_addr_1.clone(),
2501 validator_addr_1,
2502 coin(111, BONDED_DENOM),
2503 )
2504 );
2505
2506 env.block.time = env.block.time.plus_seconds(60);
2508 env.router
2509 .staking
2510 .process_queue(&env.api, &mut env.storage, &env.router, &env.block)
2511 .unwrap();
2512 let balance =
2513 QuerierWrapper::<Empty>::new(&env.router.querier(&env.api, &env.storage, &env.block))
2514 .query_balance(delegator_addr_1, BONDED_DENOM)
2515 .unwrap();
2516 assert_eq!(55, balance.amount.u128());
2517 }
2518
2519 #[test]
2520 fn rewards_initial_wait() {
2521 let mut env = TestEnv::new(vp(0, 100, 1), vp(0, 100, 1));
2522
2523 let validator_addr_1 = env.validator_addr_1();
2524 let delegator_addr_1 = env.delegator_addr_1();
2525
2526 init_balance(&mut env, &delegator_addr_1, 100);
2528
2529 env.block.time = env.block.time.plus_seconds(YEAR);
2531
2532 execute_stake(
2534 &mut env,
2535 delegator_addr_1.clone(),
2536 StakingMsg::Delegate {
2537 validator: validator_addr_1.to_string(),
2538 amount: coin(100, BONDED_DENOM),
2539 },
2540 )
2541 .unwrap();
2542
2543 env.block.time = env.block.time.plus_seconds(YEAR);
2545
2546 let response: DelegationResponse = query_stake(
2548 &env,
2549 StakingQuery::Delegation {
2550 delegator: delegator_addr_1.to_string(),
2551 validator: validator_addr_1,
2552 },
2553 )
2554 .unwrap();
2555
2556 assert_eq!(
2557 response.delegation.unwrap().accumulated_rewards,
2558 vec![coin(10, BONDED_DENOM)] );
2560 }
2561}