penumbra_sdk_stake/component/action_handler/
undelegate.rs1use anyhow::Result;
2use async_trait::async_trait;
3use cnidarium::StateWrite;
4use penumbra_sdk_proto::{DomainType as _, StateWriteProto};
5use penumbra_sdk_sct::component::clock::EpochRead;
6use penumbra_sdk_shielded_pool::component::AssetRegistry;
7
8use crate::{
9 component::action_handler::ActionHandler,
10 component::{validator_handler::ValidatorDataRead, StateWriteExt as _},
11 event, Undelegate,
12};
13
14#[async_trait]
15impl ActionHandler for Undelegate {
16 type CheckStatelessContext = ();
17 async fn check_stateless(&self, _context: ()) -> Result<()> {
18 Ok(())
19 }
20
21 async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
22 let u = self;
26
27 let current_epoch = state.get_current_epoch().await?;
31 let prepared_for_current_epoch = u.from_epoch == current_epoch;
32 if !prepared_for_current_epoch {
33 tracing::error!(
34 from = ?u.from_epoch,
35 current = ?current_epoch,
36 "undelegation was prepared for a different epoch",
37 );
38 anyhow::bail!(
39 "undelegation was prepared for epoch {} but the current epoch is {}",
40 u.from_epoch.index,
41 current_epoch.index
42 );
43 }
44
45 let rate_data = state
60 .get_validator_rate(&u.validator_identity)
61 .await?
62 .ok_or_else(|| {
63 anyhow::anyhow!("unknown validator identity {}", u.validator_identity)
64 })?;
65 let expected_unbonded_amount = rate_data.unbonded_amount(u.delegation_amount);
66
67 if u.unbonded_amount != expected_unbonded_amount {
68 tracing::error!(
69 actual = %u.unbonded_amount,
70 expected = %expected_unbonded_amount,
71 "undelegation amount does not match expected amount",
72 );
73 anyhow::bail!(
74 "undelegation amount {} does not match expected amount {}",
75 u.unbonded_amount,
76 expected_unbonded_amount,
77 );
78 }
79
80 state.register_denom(&self.unbonding_token().denom()).await;
84
85 tracing::debug!(?self, "queuing undelegation for next epoch");
86 state.push_undelegation(self.clone());
87
88 state.record_proto(event::EventUndelegate::from(self).to_proto());
89
90 Ok(())
91 }
92}