pcli/command/tx/
proposal.rs1use anyhow::{Context, Result};
2
3use penumbra_sdk_app::params::AppParameters;
4use penumbra_sdk_governance::{change::ParameterChange, Proposal, ProposalPayload};
5use penumbra_sdk_proto::DomainType;
6use penumbra_sdk_transaction::TransactionPlan;
7
8use super::FeeTier;
9
10#[derive(Debug, clap::Subcommand)]
11pub enum ProposalCmd {
12 Template {
14 #[clap(long, global = true)]
16 file: Option<camino::Utf8PathBuf>,
17 #[clap(subcommand)]
19 kind: ProposalKindCmd,
20 },
21 Submit {
23 #[clap(long)]
25 file: camino::Utf8PathBuf,
26 #[clap(long, default_value = "0")]
28 source: u32,
29 #[clap(long, default_value = "")]
31 deposit_amount: String,
32 #[clap(short, long, default_value_t)]
34 fee_tier: FeeTier,
35 },
36 Withdraw {
38 proposal_id: u64,
40 #[clap(long)]
43 reason: String,
44 #[clap(long, default_value = "0")]
46 source: u32,
47 #[clap(short, long, default_value_t)]
49 fee_tier: FeeTier,
50 },
51 DepositClaim {
57 proposal_id: u64,
59 #[clap(long, default_value = "0")]
61 source: u32,
62 #[clap(short, long, default_value_t)]
64 fee_tier: FeeTier,
65 },
66}
67
68#[derive(Debug, clap::Subcommand)]
69pub enum ProposalKindCmd {
70 Signaling,
72 Emergency,
74 ParameterChange,
76 CommunityPoolSpend {
78 #[clap(long)]
83 transaction_plan: Option<camino::Utf8PathBuf>,
84 },
85 UpgradePlan,
87}
88
89impl ProposalKindCmd {
90 pub fn template_proposal(&self, app_params: &AppParameters, id: u64) -> Result<Proposal> {
92 let title = "A short title (at most 80 characters)".to_string();
93 let description = "A longer description (at most 10,000 characters)".to_string();
94 let payload = match self {
95 ProposalKindCmd::Signaling => ProposalPayload::Signaling { commit: None },
96 ProposalKindCmd::Emergency => ProposalPayload::Emergency { halt_chain: false },
97 ProposalKindCmd::ParameterChange => {
98 ProposalPayload::ParameterChange(ParameterChange::encode_parameters(
99 serde_json::value::to_value(app_params.clone())?,
100 ))
101 }
102 ProposalKindCmd::CommunityPoolSpend { transaction_plan } => {
103 if let Some(file) = transaction_plan {
104 ProposalPayload::CommunityPoolSpend {
105 transaction_plan: serde_json::from_reader(
106 std::fs::File::open(file).with_context(|| {
107 format!("Failed to open transaction plan file {:?}", file)
108 })?,
109 )
110 .with_context(|| {
111 format!("Failed to parse transaction plan file {:?}", file)
112 })?,
113 }
114 } else {
115 ProposalPayload::CommunityPoolSpend {
116 transaction_plan: TransactionPlan::default().encode_to_vec(),
117 }
118 }
119 }
120 ProposalKindCmd::UpgradePlan { .. } => ProposalPayload::UpgradePlan { height: 0 },
121 };
122
123 Ok(Proposal {
124 id,
125 title,
126 description,
127 payload,
128 })
129 }
130}
131
132impl ProposalCmd {
133 pub fn offline(&self) -> bool {
134 match self {
135 ProposalCmd::Template { .. } => false,
136 ProposalCmd::Submit { .. } => false,
137 ProposalCmd::Withdraw { .. } => false,
138 ProposalCmd::DepositClaim { .. } => false,
139 }
140 }
141}