penumbra_sdk_governance/action_handler/
withdraw.rs1use anyhow::Result;
2use async_trait::async_trait;
3use cnidarium::StateWrite;
4use penumbra_sdk_proto::StateWriteProto as _;
5use penumbra_sdk_shielded_pool::component::AssetRegistry;
6
7use crate::{
8 action_handler::ActionHandler,
9 component::{StateReadExt as _, StateWriteExt},
10 event,
11 proposal_state::State as ProposalState,
12 ProposalNft, ProposalWithdraw,
13};
14
15#[async_trait]
16impl ActionHandler for ProposalWithdraw {
17 type CheckStatelessContext = ();
18 async fn check_stateless(&self, _context: ()) -> Result<()> {
19 const PROPOSAL_WITHDRAWAL_REASON_LIMIT: usize = 80;
21
22 if self.reason.len() > PROPOSAL_WITHDRAWAL_REASON_LIMIT {
23 anyhow::bail!(
24 "proposal withdrawal reason must fit within {PROPOSAL_WITHDRAWAL_REASON_LIMIT} characters"
25 );
26 }
27
28 Ok(())
29 }
30
31 async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
32 state.check_proposal_votable(self.proposal).await?;
34
35 let ProposalWithdraw { proposal, reason } = self;
36
37 state.put_proposal_state(
39 *proposal,
40 ProposalState::Withdrawn {
41 reason: reason.clone(),
42 },
43 );
44
45 state
47 .register_denom(&ProposalNft::unbonding_deposit(*proposal).denom())
48 .await;
49
50 state.record_proto(event::proposal_withdraw(self));
51
52 tracing::debug!(proposal = %proposal, "withdrew proposal");
53
54 Ok(())
55 }
56}