penumbra_sdk_governance/proposal_deposit_claim/
action.rs1use serde::{Deserialize, Serialize};
2
3use penumbra_sdk_asset::{
4 asset::{self, Metadata},
5 Balance, Value, STAKING_TOKEN_ASSET_ID,
6};
7use penumbra_sdk_num::Amount;
8use penumbra_sdk_proto::{penumbra::core::component::governance::v1 as pb, DomainType};
9use penumbra_sdk_txhash::{EffectHash, EffectingData};
10
11use crate::proposal_state::{Outcome, Withdrawn};
12
13use crate::ProposalNft;
14
15#[derive(Debug, Clone, Serialize, Deserialize, Copy)]
17#[serde(
18 try_from = "pb::ProposalDepositClaim",
19 into = "pb::ProposalDepositClaim"
20)]
21pub struct ProposalDepositClaim {
22 pub proposal: u64,
24 pub deposit_amount: Amount,
26 pub outcome: Outcome<()>,
28}
29
30impl EffectingData for ProposalDepositClaim {
31 fn effect_hash(&self) -> EffectHash {
32 EffectHash::from_proto_effecting_data(&self.to_proto())
33 }
34}
35
36impl DomainType for ProposalDepositClaim {
37 type Proto = pb::ProposalDepositClaim;
38}
39
40impl From<ProposalDepositClaim> for pb::ProposalDepositClaim {
41 fn from(value: ProposalDepositClaim) -> pb::ProposalDepositClaim {
42 pb::ProposalDepositClaim {
43 proposal: value.proposal,
44 deposit_amount: Some(value.deposit_amount.into()),
45 outcome: Some(value.outcome.into()),
46 }
47 }
48}
49
50impl TryFrom<pb::ProposalDepositClaim> for ProposalDepositClaim {
51 type Error = anyhow::Error;
52
53 fn try_from(msg: pb::ProposalDepositClaim) -> Result<Self, Self::Error> {
54 Ok(ProposalDepositClaim {
55 proposal: msg.proposal,
56 deposit_amount: msg
57 .deposit_amount
58 .ok_or_else(|| anyhow::anyhow!("missing deposit amount in `ProposalDepositClaim`"))?
59 .try_into()?,
60 outcome: msg
61 .outcome
62 .ok_or_else(|| anyhow::anyhow!("missing outcome in `ProposalDepositClaim`"))?
63 .try_into()?,
64 })
65 }
66}
67
68impl ProposalDepositClaim {
69 pub fn balance(&self) -> Balance {
71 let deposit = Value {
72 amount: self.deposit_amount,
73 asset_id: *STAKING_TOKEN_ASSET_ID,
74 };
75
76 let (voting_or_withdrawn_proposal_denom, claimed_proposal_denom): (Metadata, Metadata) =
77 match self.outcome {
78 Outcome::Passed => (
80 ProposalNft::deposit(self.proposal).denom(),
81 ProposalNft::passed(self.proposal).denom(),
82 ),
83 Outcome::Failed {
84 withdrawn: Withdrawn::No,
85 } => (
86 ProposalNft::deposit(self.proposal).denom(),
87 ProposalNft::failed(self.proposal).denom(),
88 ),
89 Outcome::Slashed {
90 withdrawn: Withdrawn::No,
91 } => (
92 ProposalNft::deposit(self.proposal).denom(),
93 ProposalNft::slashed(self.proposal).denom(),
94 ),
95 Outcome::Failed {
97 withdrawn: Withdrawn::WithReason { .. },
98 } => (
99 ProposalNft::unbonding_deposit(self.proposal).denom(),
100 ProposalNft::failed(self.proposal).denom(),
101 ),
102 Outcome::Slashed {
103 withdrawn: Withdrawn::WithReason { .. },
104 } => (
105 ProposalNft::unbonding_deposit(self.proposal).denom(),
106 ProposalNft::slashed(self.proposal).denom(),
107 ),
108 };
109
110 let voting_or_withdrawn_proposal_nft = Value {
112 amount: Amount::from(1u64),
113 asset_id: asset::Id::from(voting_or_withdrawn_proposal_denom),
114 };
115
116 let claimed_proposal_nft = Value {
118 amount: Amount::from(1u64),
119 asset_id: asset::Id::from(claimed_proposal_denom),
120 };
121
122 let mut balance =
125 Balance::from(claimed_proposal_nft) - Balance::from(voting_or_withdrawn_proposal_nft);
126
127 if self.outcome.should_be_refunded() {
129 balance += Balance::from(deposit);
130 }
131
132 balance
133 }
134}