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, CustomQuery, Decimal, Delegation,
9 DelegationResponse, DistributionMsg, Empty, Event, FullDelegation, Querier, StakingMsg,
10 StakingQuery, Storage, Timestamp, Uint128, Validator, ValidatorResponse,
11};
12use cw_storage_plus::{Deque, Item, Map};
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use std::collections::{BTreeSet, VecDeque};
16
17#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
19pub struct StakingInfo {
20 pub bonded_denom: String,
22 pub unbonding_time: u64,
24 pub apr: Decimal,
26}
27
28impl Default for StakingInfo {
29 fn default() -> Self {
30 StakingInfo {
31 bonded_denom: "TOKEN".to_string(),
32 unbonding_time: 60,
33 apr: Decimal::percent(10),
34 }
35 }
36}
37
38#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, JsonSchema)]
40struct Shares {
41 stake: Decimal,
42 rewards: Decimal,
43}
44
45impl Shares {
46 pub fn share_of_rewards(&self, validator: &ValidatorInfo, rewards: Decimal) -> Decimal {
48 if validator.stake.is_zero() {
49 return Decimal::zero();
50 }
51 rewards * self.stake / validator.stake
52 }
53}
54
55#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
57struct ValidatorInfo {
58 stakers: BTreeSet<Addr>,
61 stake: Uint128,
63 last_rewards_calculation: Timestamp,
65}
66
67impl ValidatorInfo {
68 pub fn new(block_time: Timestamp) -> Self {
69 Self {
70 stakers: BTreeSet::new(),
71 stake: Uint128::zero(),
72 last_rewards_calculation: block_time,
73 }
74 }
75}
76
77#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
78struct Unbonding {
79 pub delegator: Addr,
80 pub validator: Addr,
81 pub amount: Uint128,
82 pub payout_at: Timestamp,
83}
84
85const STAKING_INFO: Item<StakingInfo> = Item::new("staking_info");
86const STAKES: Map<(&Addr, &Addr), Shares> = Map::new("stakes");
88const VALIDATOR_MAP: Map<&Addr, Validator> = Map::new("validator_map");
89const VALIDATORS: Deque<Validator> = Deque::new("validators");
91const VALIDATOR_INFO: Map<&Addr, ValidatorInfo> = Map::new("validator_info");
93const UNBONDING_QUEUE: Item<VecDeque<Unbonding>> = Item::new("unbonding_queue");
95const WITHDRAW_ADDRESS: Map<&Addr, Addr> = Map::new("withdraw_address");
99
100pub const NAMESPACE_STAKING: &[u8] = b"staking";
101pub const NAMESPACE_DISTRIBUTION: &[u8] = b"distribution";
103
104#[derive(Clone, std::fmt::Debug, PartialEq, Eq, JsonSchema)]
106pub enum StakingSudo {
107 Slash {
110 validator: String,
111 percentage: Decimal,
112 },
113 #[deprecated(note = "This is not needed anymore. Just call `update_block`")]
117 ProcessQueue {},
118}
119
120pub trait Staking: Module<ExecT = StakingMsg, QueryT = StakingQuery, SudoT = StakingSudo> {
121 fn process_queue<ExecC, QueryC: CustomQuery>(
126 &self,
127 api: &dyn Api,
128 storage: &mut dyn Storage,
129 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
130 block: &BlockInfo,
131 ) -> AnyResult<AppResponse>;
132}
133
134pub trait Distribution: Module<ExecT = DistributionMsg, QueryT = Empty, SudoT = Empty> {}
135
136pub struct StakeKeeper {
137 module_addr: Addr,
138}
139
140impl Default for StakeKeeper {
141 fn default() -> Self {
142 Self::new()
143 }
144}
145
146impl StakeKeeper {
147 pub fn new() -> Self {
148 StakeKeeper {
149 module_addr: Addr::unchecked("staking_module"),
151 }
152 }
153
154 pub fn setup(&self, storage: &mut dyn Storage, staking_info: StakingInfo) -> AnyResult<()> {
156 let mut storage = prefixed(storage, NAMESPACE_STAKING);
157
158 STAKING_INFO.save(&mut storage, &staking_info)?;
159 Ok(())
160 }
161
162 pub fn add_validator(
164 &self,
165 api: &dyn Api,
166 storage: &mut dyn Storage,
167 block: &BlockInfo,
168 validator: Validator,
169 ) -> AnyResult<()> {
170 let mut storage = prefixed(storage, NAMESPACE_STAKING);
171
172 let val_addr = api.addr_validate(&validator.address)?;
173 if VALIDATOR_MAP.may_load(&storage, &val_addr)?.is_some() {
174 bail!(
175 "Cannot add validator {}, since a validator with that address already exists",
176 val_addr
177 );
178 }
179
180 VALIDATOR_MAP.save(&mut storage, &val_addr, &validator)?;
181 VALIDATORS.push_back(&mut storage, &validator)?;
182 VALIDATOR_INFO.save(&mut storage, &val_addr, &ValidatorInfo::new(block.time))?;
183 Ok(())
184 }
185
186 fn get_staking_info(staking_storage: &dyn Storage) -> AnyResult<StakingInfo> {
187 Ok(STAKING_INFO.may_load(staking_storage)?.unwrap_or_default())
188 }
189
190 pub fn get_rewards(
192 &self,
193 storage: &dyn Storage,
194 block: &BlockInfo,
195 delegator: &Addr,
196 validator: &Addr,
197 ) -> AnyResult<Option<Coin>> {
198 let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
199
200 let validator_obj = match self.get_validator(&staking_storage, validator)? {
201 Some(validator) => validator,
202 None => bail!("validator {} not found", validator),
203 };
204 let shares = match STAKES.load(&staking_storage, (delegator, validator)) {
206 Ok(stakes) => stakes,
207 Err(_) => {
208 return Ok(None);
209 }
210 };
211 let validator_info = VALIDATOR_INFO.load(&staking_storage, validator)?;
212
213 Self::get_rewards_internal(
214 &staking_storage,
215 block,
216 &shares,
217 &validator_obj,
218 &validator_info,
219 )
220 .map(Some)
221 }
222
223 fn get_rewards_internal(
224 staking_storage: &dyn Storage,
225 block: &BlockInfo,
226 shares: &Shares,
227 validator: &Validator,
228 validator_info: &ValidatorInfo,
229 ) -> AnyResult<Coin> {
230 let staking_info = Self::get_staking_info(staking_storage)?;
231
232 let new_validator_rewards = Self::calculate_rewards(
234 block.time,
235 validator_info.last_rewards_calculation,
236 staking_info.apr,
237 validator.commission,
238 validator_info.stake,
239 );
240
241 let delegator_rewards =
243 shares.rewards + shares.share_of_rewards(validator_info, new_validator_rewards);
244
245 Ok(Coin {
246 denom: staking_info.bonded_denom,
247 amount: Uint128::new(1).mul_floor(delegator_rewards), })
249 }
250
251 fn calculate_rewards(
253 current_time: Timestamp,
254 since: Timestamp,
255 interest_rate: Decimal,
256 validator_commission: Decimal,
257 stake: Uint128,
258 ) -> Decimal {
259 let time_diff = current_time.minus_seconds(since.seconds()).seconds();
261
262 let reward = Decimal::from_ratio(stake, 1u128)
264 * interest_rate
265 * Decimal::from_ratio(time_diff, 1u128)
266 / Decimal::from_ratio(60u128 * 60 * 24 * 365, 1u128);
267 let commission = reward * validator_commission;
268
269 reward - commission
270 }
271
272 fn update_rewards(
276 api: &dyn Api,
277 staking_storage: &mut dyn Storage,
278 block: &BlockInfo,
279 validator: &Addr,
280 ) -> AnyResult<()> {
281 let staking_info = Self::get_staking_info(staking_storage)?;
282
283 let mut validator_info = VALIDATOR_INFO
284 .may_load(staking_storage, validator)?
285 .ok_or_else(|| anyhow!("validator does not exist"))?;
287
288 let validator_obj = VALIDATOR_MAP.load(staking_storage, validator)?;
289
290 if validator_info.last_rewards_calculation >= block.time {
291 return Ok(());
292 }
293
294 let new_rewards = Self::calculate_rewards(
295 block.time,
296 validator_info.last_rewards_calculation,
297 staking_info.apr,
298 validator_obj.commission,
299 validator_info.stake,
300 );
301
302 validator_info.last_rewards_calculation = block.time;
304 VALIDATOR_INFO.save(staking_storage, validator, &validator_info)?;
305
306 if !new_rewards.is_zero() {
308 let validator_addr = api.addr_validate(&validator_obj.address)?;
309 for staker in validator_info.stakers.iter() {
311 STAKES.update(
312 staking_storage,
313 (staker, &validator_addr),
314 |shares| -> AnyResult<_> {
315 let mut shares =
316 shares.expect("all stakers in validator_info should exist");
317 shares.rewards += shares.share_of_rewards(&validator_info, new_rewards);
318 Ok(shares)
319 },
320 )?;
321 }
322 }
323 Ok(())
324 }
325
326 fn get_validator(
328 &self,
329 staking_storage: &dyn Storage,
330 address: &Addr,
331 ) -> AnyResult<Option<Validator>> {
332 Ok(VALIDATOR_MAP.may_load(staking_storage, address)?)
333 }
334
335 fn get_validators(&self, staking_storage: &dyn Storage) -> AnyResult<Vec<Validator>> {
337 let res: Result<_, _> = VALIDATORS.iter(staking_storage)?.collect();
338 Ok(res?)
339 }
340
341 fn get_stake(
342 &self,
343 staking_storage: &dyn Storage,
344 account: &Addr,
345 validator: &Addr,
346 ) -> AnyResult<Option<Coin>> {
347 let shares = STAKES.may_load(staking_storage, (account, validator))?;
348 let staking_info = Self::get_staking_info(staking_storage)?;
349
350 Ok(shares.map(|shares| {
351 Coin {
352 denom: staking_info.bonded_denom,
353 amount: Uint128::new(1).mul_floor(shares.stake), }
355 }))
356 }
357
358 fn add_stake(
359 &self,
360 api: &dyn Api,
361 staking_storage: &mut dyn Storage,
362 block: &BlockInfo,
363 to_address: &Addr,
364 validator: &Addr,
365 amount: Coin,
366 ) -> AnyResult<()> {
367 self.validate_denom(staking_storage, &amount)?;
368 self.update_stake(
369 api,
370 staking_storage,
371 block,
372 to_address,
373 validator,
374 amount.amount,
375 false,
376 )
377 }
378
379 fn remove_stake(
380 &self,
381 api: &dyn Api,
382 staking_storage: &mut dyn Storage,
383 block: &BlockInfo,
384 from_address: &Addr,
385 validator: &Addr,
386 amount: Coin,
387 ) -> AnyResult<()> {
388 self.validate_denom(staking_storage, &amount)?;
389 self.update_stake(
390 api,
391 staking_storage,
392 block,
393 from_address,
394 validator,
395 amount.amount,
396 true,
397 )
398 }
399
400 fn update_stake(
401 &self,
402 api: &dyn Api,
403 staking_storage: &mut dyn Storage,
404 block: &BlockInfo,
405 delegator: &Addr,
406 validator: &Addr,
407 amount: impl Into<Uint128>,
408 sub: bool,
409 ) -> AnyResult<()> {
410 let amount = amount.into();
411
412 Self::update_rewards(api, staking_storage, block, validator)?;
414
415 let mut validator_info = VALIDATOR_INFO
417 .may_load(staking_storage, validator)?
418 .unwrap_or_else(|| ValidatorInfo::new(block.time));
419 let shares = STAKES.may_load(staking_storage, (delegator, validator))?;
420 let mut shares = if sub {
421 shares.ok_or_else(|| anyhow!("no delegation for (address, validator) tuple"))?
424 } else {
425 shares.unwrap_or_default()
426 };
427
428 let amount_dec = Decimal::from_ratio(amount, 1u128);
429 if sub {
430 if amount_dec > shares.stake {
432 bail!("invalid shares amount");
433 }
434 shares.stake -= amount_dec;
435 validator_info.stake = validator_info.stake.checked_sub(amount)?;
436 } else {
437 shares.stake += amount_dec;
438 validator_info.stake = validator_info.stake.checked_add(amount)?;
439 }
440
441 if shares.stake.is_zero() {
443 STAKES.remove(staking_storage, (delegator, validator));
445 validator_info.stakers.remove(delegator);
446 } else {
447 STAKES.save(staking_storage, (delegator, validator), &shares)?;
448 validator_info.stakers.insert(delegator.clone());
449 }
450 VALIDATOR_INFO.save(staking_storage, validator, &validator_info)?;
452
453 Ok(())
454 }
455
456 fn slash(
457 &self,
458 api: &dyn Api,
459 staking_storage: &mut dyn Storage,
460 block: &BlockInfo,
461 validator: &Addr,
462 percentage: Decimal,
463 ) -> AnyResult<()> {
464 Self::update_rewards(api, staking_storage, block, validator)?;
466
467 let mut validator_info = VALIDATOR_INFO
469 .may_load(staking_storage, validator)?
470 .unwrap();
471
472 let remaining_percentage = Decimal::one() - percentage;
473 validator_info.stake = validator_info.stake.mul_floor(remaining_percentage);
474
475 if validator_info.stake.is_zero() {
477 for delegator in validator_info.stakers.iter() {
479 STAKES.remove(staking_storage, (delegator, validator));
480 }
481 validator_info.stakers.clear();
482 } else {
483 for delegator in validator_info.stakers.iter() {
485 STAKES.update(
486 staking_storage,
487 (delegator, validator),
488 |stake| -> AnyResult<_> {
489 let mut stake = stake.expect("all stakers in validator_info should exist");
490 stake.stake *= remaining_percentage;
491
492 Ok(stake)
493 },
494 )?;
495 }
496 }
497 let mut unbonding_queue = UNBONDING_QUEUE
499 .may_load(staking_storage)?
500 .unwrap_or_default();
501 #[allow(clippy::op_ref)]
502 unbonding_queue
503 .iter_mut()
504 .filter(|ub| &ub.validator == validator)
505 .for_each(|ub| {
506 ub.amount = ub.amount.mul_floor(remaining_percentage);
507 });
508 UNBONDING_QUEUE.save(staking_storage, &unbonding_queue)?;
509
510 VALIDATOR_INFO.save(staking_storage, validator, &validator_info)?;
511 Ok(())
512 }
513
514 fn validate_denom(&self, staking_storage: &dyn Storage, amount: &Coin) -> AnyResult<()> {
516 let staking_info = Self::get_staking_info(staking_storage)?;
517 ensure_eq!(
518 amount.denom,
519 staking_info.bonded_denom,
520 anyhow!(
521 "cannot delegate coins of denominator {}, only of {}",
522 amount.denom,
523 staking_info.bonded_denom
524 )
525 );
526 Ok(())
527 }
528
529 fn validate_percentage(&self, percentage: Decimal) -> AnyResult<()> {
531 ensure!(percentage <= Decimal::one(), anyhow!("expected percentage"));
532 Ok(())
533 }
534
535 fn process_queue<ExecC, QueryC: CustomQuery>(
536 &self,
537 api: &dyn Api,
538 storage: &mut dyn Storage,
539 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
540 block: &BlockInfo,
541 ) -> AnyResult<AppResponse> {
542 let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
543 let mut unbonding_queue = UNBONDING_QUEUE
544 .may_load(&staking_storage)?
545 .unwrap_or_default();
546 loop {
547 let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
548 match unbonding_queue.front() {
549 Some(Unbonding { payout_at, .. }) if payout_at <= &block.time => {
551 let Unbonding {
553 delegator,
554 validator,
555 amount,
556 ..
557 } = unbonding_queue.pop_front().unwrap();
558
559 let delegation = self
561 .get_stake(&staking_storage, &delegator, &validator)?
562 .map(|mut stake| {
563 stake.amount += unbonding_queue
565 .iter()
566 .filter(|u| u.delegator == delegator && u.validator == validator)
567 .map(|u| u.amount)
568 .sum::<Uint128>();
569 stake
570 });
571 match delegation {
572 Some(delegation) if delegation.amount.is_zero() => {
573 STAKES.remove(&mut staking_storage, (&delegator, &validator));
574 }
575 None => STAKES.remove(&mut staking_storage, (&delegator, &validator)),
576 _ => {}
577 }
578
579 let staking_info = Self::get_staking_info(&staking_storage)?;
580 if !amount.is_zero() {
581 router.execute(
582 api,
583 storage,
584 block,
585 self.module_addr.clone(),
586 BankMsg::Send {
587 to_address: delegator.into_string(),
588 amount: vec![coin(amount.u128(), &staking_info.bonded_denom)],
589 }
590 .into(),
591 )?;
592 }
593 }
594 _ => break,
595 }
596 }
597 let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
598 UNBONDING_QUEUE.save(&mut staking_storage, &unbonding_queue)?;
599 Ok(AppResponse::default())
600 }
601}
602
603impl Staking for StakeKeeper {
604 fn process_queue<ExecC, QueryC: CustomQuery>(
605 &self,
606 api: &dyn Api,
607 storage: &mut dyn Storage,
608 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
609 block: &BlockInfo,
610 ) -> AnyResult<AppResponse> {
611 self.process_queue(api, storage, router, block)
612 }
613}
614
615impl Module for StakeKeeper {
616 type ExecT = StakingMsg;
617 type QueryT = StakingQuery;
618 type SudoT = StakingSudo;
619
620 fn execute<ExecC, QueryC: CustomQuery>(
621 &self,
622 api: &dyn Api,
623 storage: &mut dyn Storage,
624 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
625 block: &BlockInfo,
626 sender: Addr,
627 msg: StakingMsg,
628 ) -> AnyResult<AppResponse> {
629 let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
630 match msg {
631 StakingMsg::Delegate { validator, amount } => {
632 let validator = api.addr_validate(&validator)?;
633
634 if amount.amount.is_zero() {
636 bail!("invalid delegation amount");
637 }
638
639 let events = vec![Event::new("delegate")
641 .add_attribute("validator", &validator)
642 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))
643 .add_attribute("new_shares", amount.amount.to_string())]; self.add_stake(
645 api,
646 &mut staking_storage,
647 block,
648 &sender,
649 &validator,
650 amount.clone(),
651 )?;
652 router.execute(
654 api,
655 storage,
656 block,
657 sender,
658 BankMsg::Send {
659 to_address: self.module_addr.to_string(),
660 amount: vec![amount],
661 }
662 .into(),
663 )?;
664 Ok(AppResponse { events, data: None })
665 }
666 StakingMsg::Undelegate { validator, amount } => {
667 let validator = api.addr_validate(&validator)?;
668 self.validate_denom(&staking_storage, &amount)?;
669
670 if amount.amount.is_zero() {
672 bail!("invalid shares amount");
673 }
674
675 let events = vec![Event::new("unbond")
677 .add_attribute("validator", &validator)
678 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))
679 .add_attribute("completion_time", "2022-09-27T14:00:00+00:00")]; self.remove_stake(
681 api,
682 &mut staking_storage,
683 block,
684 &sender,
685 &validator,
686 amount.clone(),
687 )?;
688 let staking_info = Self::get_staking_info(&staking_storage)?;
690 let mut unbonding_queue = UNBONDING_QUEUE
691 .may_load(&staking_storage)?
692 .unwrap_or_default();
693 unbonding_queue.push_back(Unbonding {
694 delegator: sender.clone(),
695 validator,
696 amount: amount.amount,
697 payout_at: block.time.plus_seconds(staking_info.unbonding_time),
698 });
699 UNBONDING_QUEUE.save(&mut staking_storage, &unbonding_queue)?;
700 Ok(AppResponse { events, data: None })
701 }
702 StakingMsg::Redelegate {
703 src_validator,
704 dst_validator,
705 amount,
706 } => {
707 let src_validator = api.addr_validate(&src_validator)?;
708 let dst_validator = api.addr_validate(&dst_validator)?;
709 let events = vec![Event::new("redelegate")
711 .add_attribute("source_validator", &src_validator)
712 .add_attribute("destination_validator", &dst_validator)
713 .add_attribute("amount", format!("{}{}", amount.amount, amount.denom))];
714
715 self.remove_stake(
716 api,
717 &mut staking_storage,
718 block,
719 &sender,
720 &src_validator,
721 amount.clone(),
722 )?;
723 self.add_stake(
724 api,
725 &mut staking_storage,
726 block,
727 &sender,
728 &dst_validator,
729 amount,
730 )?;
731
732 Ok(AppResponse { events, data: None })
733 }
734 m => bail!("Unsupported staking message: {:?}", m),
735 }
736 }
737
738 fn sudo<ExecC, QueryC: CustomQuery>(
739 &self,
740 api: &dyn Api,
741 storage: &mut dyn Storage,
742 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
743 block: &BlockInfo,
744 msg: StakingSudo,
745 ) -> AnyResult<AppResponse> {
746 match msg {
747 StakingSudo::Slash {
748 validator,
749 percentage,
750 } => {
751 let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
752 let validator = api.addr_validate(&validator)?;
753 self.validate_percentage(percentage)?;
754
755 self.slash(api, &mut staking_storage, block, &validator, percentage)?;
756
757 Ok(AppResponse::default())
758 }
759 #[allow(deprecated)]
760 StakingSudo::ProcessQueue {} => self.process_queue(api, storage, router, block),
761 }
762 }
763
764 fn query(
765 &self,
766 api: &dyn Api,
767 storage: &dyn Storage,
768 _querier: &dyn Querier,
769 block: &BlockInfo,
770 request: StakingQuery,
771 ) -> AnyResult<Binary> {
772 let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
773 match request {
774 StakingQuery::BondedDenom {} => Ok(to_json_binary(&BondedDenomResponse::new(
775 Self::get_staking_info(&staking_storage)?.bonded_denom,
776 ))?),
777 StakingQuery::AllDelegations { delegator } => {
778 let delegator = api.addr_validate(&delegator)?;
779 let validators = self.get_validators(&staking_storage)?;
780
781 let res: AnyResult<Vec<Delegation>> =
782 validators
783 .into_iter()
784 .filter_map(|validator| {
785 let delegator = delegator.clone();
786 let amount = self
787 .get_stake(
788 &staking_storage,
789 &delegator,
790 &Addr::unchecked(&validator.address),
791 )
792 .transpose()?;
793
794 Some(amount.map(|amount| {
795 Delegation::new(delegator, validator.address, amount)
796 }))
797 })
798 .collect();
799
800 Ok(to_json_binary(&AllDelegationsResponse::new(res?))?)
801 }
802 StakingQuery::Delegation {
803 delegator,
804 validator,
805 } => {
806 let validator_addr = Addr::unchecked(&validator);
807 let validator_obj = match self.get_validator(&staking_storage, &validator_addr)? {
808 Some(validator) => validator,
809 None => bail!("non-existent validator {}", validator),
810 };
811 let delegator = api.addr_validate(&delegator)?;
812
813 let shares = STAKES
814 .may_load(&staking_storage, (&delegator, &validator_addr))?
815 .unwrap_or_default();
816
817 let validator_info = VALIDATOR_INFO.load(&staking_storage, &validator_addr)?;
818 let reward = Self::get_rewards_internal(
819 &staking_storage,
820 block,
821 &shares,
822 &validator_obj,
823 &validator_info,
824 )?;
825 let staking_info = Self::get_staking_info(&staking_storage)?;
826
827 let amount = coin(
828 (Uint128::new(1).mul_floor(shares.stake)).u128(),
829 staking_info.bonded_denom,
830 );
831
832 let full_delegation_response = if amount.amount.is_zero() {
833 DelegationResponse::new(None)
835 } else {
836 DelegationResponse::new(Some(FullDelegation::new(
837 delegator,
838 validator,
839 amount.clone(),
840 amount, if reward.amount.is_zero() {
842 vec![]
843 } else {
844 vec![reward]
845 },
846 )))
847 };
848
849 let res = to_json_binary(&full_delegation_response)?;
850 Ok(res)
851 }
852 StakingQuery::AllValidators {} => Ok(to_json_binary(&AllValidatorsResponse::new(
853 self.get_validators(&staking_storage)?,
854 ))?),
855 StakingQuery::Validator { address } => Ok(to_json_binary(&ValidatorResponse::new(
856 self.get_validator(&staking_storage, &Addr::unchecked(address))?,
857 ))?),
858 q => bail!("Unsupported staking sudo message: {:?}", q),
859 }
860 }
861}
862
863#[derive(Default)]
864pub struct DistributionKeeper {}
865
866impl DistributionKeeper {
867 pub fn new() -> Self {
868 DistributionKeeper {}
869 }
870
871 pub fn remove_rewards(
873 &self,
874 api: &dyn Api,
875 storage: &mut dyn Storage,
876 block: &BlockInfo,
877 delegator: &Addr,
878 validator: &Addr,
879 ) -> AnyResult<Uint128> {
880 let mut staking_storage = prefixed(storage, NAMESPACE_STAKING);
881 StakeKeeper::update_rewards(api, &mut staking_storage, block, validator)?;
883
884 let mut shares = STAKES.load(&staking_storage, (delegator, validator))?;
886 let rewards = Uint128::new(1).mul_floor(shares.rewards); shares.rewards = Decimal::zero();
890 STAKES.save(&mut staking_storage, (delegator, validator), &shares)?;
891
892 Ok(rewards)
893 }
894
895 pub fn get_withdraw_address(storage: &dyn Storage, delegator: &Addr) -> AnyResult<Addr> {
896 Ok(match WITHDRAW_ADDRESS.may_load(storage, delegator)? {
897 Some(a) => a,
898 None => delegator.clone(),
899 })
900 }
901
902 pub fn set_withdraw_address(
904 storage: &mut dyn Storage,
905 delegator: &Addr,
906 withdraw_address: &Addr,
907 ) -> AnyResult<()> {
908 if delegator == withdraw_address {
909 WITHDRAW_ADDRESS.remove(storage, delegator);
910 Ok(())
911 } else {
912 WITHDRAW_ADDRESS
915 .save(storage, delegator, withdraw_address)
916 .map_err(|e| e.into())
917 }
918 }
919}
920
921impl Distribution for DistributionKeeper {}
922
923impl Module for DistributionKeeper {
924 type ExecT = DistributionMsg;
925 type QueryT = Empty;
926 type SudoT = Empty;
927
928 fn execute<ExecC, QueryC: CustomQuery>(
929 &self,
930 api: &dyn Api,
931 storage: &mut dyn Storage,
932 router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
933 block: &BlockInfo,
934 sender: Addr,
935 msg: DistributionMsg,
936 ) -> AnyResult<AppResponse> {
937 match msg {
938 DistributionMsg::WithdrawDelegatorReward { validator } => {
939 let validator_addr = api.addr_validate(&validator)?;
940
941 let rewards = self.remove_rewards(api, storage, block, &sender, &validator_addr)?;
942
943 let staking_storage = prefixed_read(storage, NAMESPACE_STAKING);
944 let distribution_storage = prefixed_read(storage, NAMESPACE_DISTRIBUTION);
945 let staking_info = StakeKeeper::get_staking_info(&staking_storage)?;
946 let receiver = Self::get_withdraw_address(&distribution_storage, &sender)?;
947 router.sudo(
949 api,
950 storage,
951 block,
952 BankSudo::Mint {
953 to_address: receiver.into_string(),
954 amount: vec![Coin {
955 amount: rewards,
956 denom: staking_info.bonded_denom.clone(),
957 }],
958 }
959 .into(),
960 )?;
961
962 let events = vec![Event::new("withdraw_delegator_reward")
963 .add_attribute("validator", &validator)
964 .add_attribute("sender", &sender)
965 .add_attribute(
966 "amount",
967 format!("{}{}", rewards, staking_info.bonded_denom),
968 )];
969 Ok(AppResponse { events, data: None })
970 }
971 DistributionMsg::SetWithdrawAddress { address } => {
972 let address = api.addr_validate(&address)?;
973 let storage = &mut prefixed(storage, NAMESPACE_DISTRIBUTION);
975 Self::set_withdraw_address(storage, &sender, &address)?;
976 Ok(AppResponse {
977 data: None,
978 events: vec![Event::new("set_withdraw_address")
980 .add_attribute("withdraw_address", address)],
981 })
982 }
983 m => bail!("Unsupported distribution message: {:?}", m),
984 }
985 }
986
987 fn sudo<ExecC, QueryC>(
988 &self,
989 _api: &dyn Api,
990 _storage: &mut dyn Storage,
991 _router: &dyn CosmosRouter<ExecC = ExecC, QueryC = QueryC>,
992 _block: &BlockInfo,
993 _msg: Empty,
994 ) -> AnyResult<AppResponse> {
995 bail!("Something went wrong - Distribution doesn't have sudo messages")
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}