1use anyhow::anyhow;
2use penumbra_sdk_auction::auction::dutch::actions::{
3 ActionDutchAuctionEnd, ActionDutchAuctionSchedule, ActionDutchAuctionWithdraw,
4};
5use penumbra_sdk_txhash::{EffectHash, EffectingData};
6use std::convert::{TryFrom, TryInto};
7
8use penumbra_sdk_asset::balance;
9use penumbra_sdk_proto::{core::transaction::v1 as pb, DomainType};
10
11use crate::{ActionView, IsAction, TransactionPerspective};
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, Debug, Deserialize, Serialize)]
16#[serde(try_from = "pb::Action", into = "pb::Action")]
17#[allow(clippy::large_enum_variant)]
18pub enum Action {
19 Output(penumbra_sdk_shielded_pool::Output),
20 Spend(penumbra_sdk_shielded_pool::Spend),
21 ValidatorDefinition(penumbra_sdk_stake::validator::Definition),
22 IbcRelay(penumbra_sdk_ibc::IbcRelay),
23 Swap(penumbra_sdk_dex::swap::Swap),
24 SwapClaim(penumbra_sdk_dex::swap_claim::SwapClaim),
25 ProposalSubmit(penumbra_sdk_governance::ProposalSubmit),
26 ProposalWithdraw(penumbra_sdk_governance::ProposalWithdraw),
27 DelegatorVote(penumbra_sdk_governance::DelegatorVote),
28 ValidatorVote(penumbra_sdk_governance::ValidatorVote),
29 ProposalDepositClaim(penumbra_sdk_governance::ProposalDepositClaim),
30
31 PositionOpen(penumbra_sdk_dex::lp::action::PositionOpen),
32 PositionClose(penumbra_sdk_dex::lp::action::PositionClose),
33 PositionWithdraw(penumbra_sdk_dex::lp::action::PositionWithdraw),
34
35 Delegate(penumbra_sdk_stake::Delegate),
36 Undelegate(penumbra_sdk_stake::Undelegate),
37 UndelegateClaim(penumbra_sdk_stake::UndelegateClaim),
38
39 Ics20Withdrawal(penumbra_sdk_shielded_pool::Ics20Withdrawal),
40
41 CommunityPoolSpend(penumbra_sdk_community_pool::CommunityPoolSpend),
42 CommunityPoolOutput(penumbra_sdk_community_pool::CommunityPoolOutput),
43 CommunityPoolDeposit(penumbra_sdk_community_pool::CommunityPoolDeposit),
44
45 ActionDutchAuctionSchedule(ActionDutchAuctionSchedule),
46 ActionDutchAuctionEnd(ActionDutchAuctionEnd),
47 ActionDutchAuctionWithdraw(ActionDutchAuctionWithdraw),
48}
49
50impl EffectingData for Action {
51 fn effect_hash(&self) -> EffectHash {
52 match self {
53 Action::Output(output) => output.effect_hash(),
54 Action::Spend(spend) => spend.effect_hash(),
55 Action::Delegate(delegate) => delegate.effect_hash(),
56 Action::Undelegate(undelegate) => undelegate.effect_hash(),
57 Action::UndelegateClaim(claim) => claim.effect_hash(),
58 Action::ProposalSubmit(submit) => submit.effect_hash(),
59 Action::ProposalWithdraw(withdraw) => withdraw.effect_hash(),
60 Action::ProposalDepositClaim(claim) => claim.effect_hash(),
61 Action::DelegatorVote(vote) => vote.effect_hash(),
62 Action::ValidatorVote(vote) => vote.effect_hash(),
63 Action::SwapClaim(swap_claim) => swap_claim.effect_hash(),
64 Action::Swap(swap) => swap.effect_hash(),
65 Action::ValidatorDefinition(defn) => defn.effect_hash(),
66 Action::IbcRelay(payload) => payload.effect_hash(),
67 Action::PositionOpen(p) => p.effect_hash(),
68 Action::PositionClose(p) => p.effect_hash(),
69 Action::PositionWithdraw(p) => p.effect_hash(),
70 Action::Ics20Withdrawal(w) => w.effect_hash(),
71 Action::CommunityPoolSpend(d) => d.effect_hash(),
72 Action::CommunityPoolOutput(d) => d.effect_hash(),
73 Action::CommunityPoolDeposit(d) => d.effect_hash(),
74 Action::ActionDutchAuctionSchedule(a) => a.effect_hash(),
75 Action::ActionDutchAuctionEnd(a) => a.effect_hash(),
76 Action::ActionDutchAuctionWithdraw(a) => a.effect_hash(),
77 }
78 }
79}
80
81impl Action {
82 pub fn create_span(&self, idx: usize) -> tracing::Span {
86 match self {
87 Action::Output(_) => tracing::info_span!("Output", ?idx),
88 Action::Spend(_) => tracing::info_span!("Spend", ?idx),
89 Action::ValidatorDefinition(_) => {
90 tracing::info_span!("ValidatorDefinition", ?idx)
91 }
92 Action::IbcRelay(msg) => {
93 let action_span = tracing::info_span!("IbcAction", ?idx);
96 msg.create_span(&action_span)
97 }
98 Action::Swap(_) => tracing::info_span!("Swap", ?idx),
99 Action::SwapClaim(_) => tracing::info_span!("SwapClaim", ?idx),
100 Action::ProposalSubmit(_) => tracing::info_span!("ProposalSubmit", ?idx),
101 Action::ProposalWithdraw(_) => {
102 tracing::info_span!("ProposalWithdraw", ?idx)
103 }
104 Action::DelegatorVote(_) => tracing::info_span!("DelegatorVote", ?idx),
105 Action::ValidatorVote(_) => tracing::info_span!("ValidatorVote", ?idx),
106 Action::ProposalDepositClaim(_) => {
107 tracing::info_span!("ProposalDepositClaim", ?idx)
108 }
109 Action::PositionOpen(_) => tracing::info_span!("PositionOpen", ?idx),
110 Action::PositionClose(_) => tracing::info_span!("PositionClose", ?idx),
111 Action::PositionWithdraw(_) => {
112 tracing::info_span!("PositionWithdraw", ?idx)
113 }
114 Action::Delegate(_) => tracing::info_span!("Delegate", ?idx),
115 Action::Undelegate(_) => tracing::info_span!("Undelegate", ?idx),
116 Action::UndelegateClaim(_) => tracing::info_span!("UndelegateClaim", ?idx),
117 Action::Ics20Withdrawal(_) => tracing::info_span!("Ics20Withdrawal", ?idx),
118 Action::CommunityPoolDeposit(_) => tracing::info_span!("CommunityPoolDeposit", ?idx),
119 Action::CommunityPoolSpend(_) => tracing::info_span!("CommunityPoolSpend", ?idx),
120 Action::CommunityPoolOutput(_) => tracing::info_span!("CommunityPoolOutput", ?idx),
121 Action::ActionDutchAuctionSchedule(_) => {
122 tracing::info_span!("ActionDutchAuctionSchedule", ?idx)
123 }
124 Action::ActionDutchAuctionEnd(_) => tracing::info_span!("ActionDutchAuctionEnd", ?idx),
125 Action::ActionDutchAuctionWithdraw(_) => {
126 tracing::info_span!("ActionDutchAuctionWithdraw", ?idx)
127 }
128 }
129 }
130
131 pub fn variant_index(&self) -> usize {
133 match self {
134 Action::Spend(_) => 1,
135 Action::Output(_) => 2,
136 Action::Swap(_) => 3,
137 Action::SwapClaim(_) => 4,
138 Action::ValidatorDefinition(_) => 16,
139 Action::IbcRelay(_) => 17,
140 Action::ProposalSubmit(_) => 18,
141 Action::ProposalWithdraw(_) => 19,
142 Action::ValidatorVote(_) => 20,
143 Action::DelegatorVote(_) => 21,
144 Action::ProposalDepositClaim(_) => 22,
145 Action::PositionOpen(_) => 30,
146 Action::PositionClose(_) => 31,
147 Action::PositionWithdraw(_) => 32,
148 Action::Delegate(_) => 40,
149 Action::Undelegate(_) => 41,
150 Action::UndelegateClaim(_) => 42,
151 Action::CommunityPoolSpend(_) => 50,
152 Action::CommunityPoolOutput(_) => 51,
153 Action::CommunityPoolDeposit(_) => 52,
154 Action::Ics20Withdrawal(_) => 200,
155 Action::ActionDutchAuctionSchedule(_) => 53,
156 Action::ActionDutchAuctionEnd(_) => 54,
157 Action::ActionDutchAuctionWithdraw(_) => 55,
158 }
159 }
160}
161
162impl IsAction for Action {
163 fn balance_commitment(&self) -> balance::Commitment {
164 match self {
165 Action::Output(output) => output.balance_commitment(),
166 Action::Spend(spend) => spend.balance_commitment(),
167 Action::Delegate(delegate) => delegate.balance_commitment(),
168 Action::Undelegate(undelegate) => undelegate.balance_commitment(),
169 Action::UndelegateClaim(undelegate_claim) => undelegate_claim.balance_commitment(),
170 Action::Swap(swap) => swap.balance_commitment(),
171 Action::SwapClaim(swap_claim) => swap_claim.balance_commitment(),
172 Action::ProposalSubmit(submit) => submit.balance_commitment(),
173 Action::ProposalWithdraw(withdraw) => withdraw.balance_commitment(),
174 Action::DelegatorVote(delegator_vote) => delegator_vote.balance_commitment(),
175 Action::ValidatorVote(validator_vote) => validator_vote.balance_commitment(),
176 Action::ProposalDepositClaim(p) => p.balance_commitment(),
177 Action::PositionOpen(p) => p.balance_commitment(),
178 Action::PositionClose(p) => p.balance_commitment(),
179 Action::PositionWithdraw(p) => p.balance_commitment(),
180 Action::Ics20Withdrawal(withdrawal) => withdrawal.balance_commitment(),
181 Action::CommunityPoolDeposit(deposit) => deposit.balance_commitment(),
182 Action::CommunityPoolSpend(spend) => spend.balance_commitment(),
183 Action::CommunityPoolOutput(output) => output.balance_commitment(),
184 Action::IbcRelay(x) => x.balance_commitment(),
187 Action::ValidatorDefinition(_) => balance::Commitment::default(),
188 Action::ActionDutchAuctionSchedule(action) => action.balance_commitment(),
189 Action::ActionDutchAuctionEnd(action) => action.balance_commitment(),
190 Action::ActionDutchAuctionWithdraw(action) => action.balance_commitment(),
191 }
192 }
193
194 fn view_from_perspective(&self, txp: &TransactionPerspective) -> ActionView {
195 match self {
196 Action::Swap(x) => x.view_from_perspective(txp),
197 Action::SwapClaim(x) => x.view_from_perspective(txp),
198 Action::Output(x) => x.view_from_perspective(txp),
199 Action::Spend(x) => x.view_from_perspective(txp),
200 Action::Delegate(x) => x.view_from_perspective(txp),
201 Action::Undelegate(x) => x.view_from_perspective(txp),
202 Action::UndelegateClaim(x) => x.view_from_perspective(txp),
203 Action::ProposalSubmit(x) => x.view_from_perspective(txp),
204 Action::ProposalWithdraw(x) => x.view_from_perspective(txp),
205 Action::DelegatorVote(x) => x.view_from_perspective(txp),
206 Action::ValidatorVote(x) => x.view_from_perspective(txp),
207 Action::ProposalDepositClaim(x) => x.view_from_perspective(txp),
208 Action::PositionOpen(x) => x.view_from_perspective(txp),
209 Action::PositionClose(x) => x.view_from_perspective(txp),
210 Action::PositionWithdraw(x) => x.view_from_perspective(txp),
211 Action::Ics20Withdrawal(x) => x.view_from_perspective(txp),
212 Action::CommunityPoolSpend(x) => x.view_from_perspective(txp),
213 Action::CommunityPoolOutput(x) => x.view_from_perspective(txp),
214 Action::CommunityPoolDeposit(x) => x.view_from_perspective(txp),
215 Action::ValidatorDefinition(x) => ActionView::ValidatorDefinition(x.to_owned()),
216 Action::IbcRelay(x) => ActionView::IbcRelay(x.to_owned()),
217 Action::ActionDutchAuctionSchedule(x) => x.view_from_perspective(txp),
218 Action::ActionDutchAuctionEnd(x) => x.view_from_perspective(txp),
219 Action::ActionDutchAuctionWithdraw(x) => x.view_from_perspective(txp),
220 }
221 }
222}
223
224impl DomainType for Action {
225 type Proto = pb::Action;
226}
227
228impl From<Action> for pb::Action {
229 fn from(msg: Action) -> Self {
230 match msg {
231 Action::Output(inner) => pb::Action {
232 action: Some(pb::action::Action::Output(inner.into())),
233 },
234 Action::Spend(inner) => pb::Action {
235 action: Some(pb::action::Action::Spend(inner.into())),
236 },
237 Action::Delegate(inner) => pb::Action {
238 action: Some(pb::action::Action::Delegate(inner.into())),
239 },
240 Action::Undelegate(inner) => pb::Action {
241 action: Some(pb::action::Action::Undelegate(inner.into())),
242 },
243 Action::UndelegateClaim(inner) => pb::Action {
244 action: Some(pb::action::Action::UndelegateClaim(inner.into())),
245 },
246 Action::ValidatorDefinition(inner) => pb::Action {
247 action: Some(pb::action::Action::ValidatorDefinition(inner.into())),
248 },
249 Action::SwapClaim(inner) => pb::Action {
250 action: Some(pb::action::Action::SwapClaim(inner.into())),
251 },
252 Action::Swap(inner) => pb::Action {
253 action: Some(pb::action::Action::Swap(inner.into())),
254 },
255 Action::IbcRelay(inner) => pb::Action {
256 action: Some(pb::action::Action::IbcRelayAction(inner.into())),
257 },
258 Action::ProposalSubmit(inner) => pb::Action {
259 action: Some(pb::action::Action::ProposalSubmit(inner.into())),
260 },
261 Action::ProposalWithdraw(inner) => pb::Action {
262 action: Some(pb::action::Action::ProposalWithdraw(inner.into())),
263 },
264 Action::DelegatorVote(inner) => pb::Action {
265 action: Some(pb::action::Action::DelegatorVote(inner.into())),
266 },
267 Action::ValidatorVote(inner) => pb::Action {
268 action: Some(pb::action::Action::ValidatorVote(inner.into())),
269 },
270 Action::ProposalDepositClaim(inner) => pb::Action {
271 action: Some(pb::action::Action::ProposalDepositClaim(inner.into())),
272 },
273 Action::PositionOpen(inner) => pb::Action {
274 action: Some(pb::action::Action::PositionOpen(inner.into())),
275 },
276 Action::PositionClose(inner) => pb::Action {
277 action: Some(pb::action::Action::PositionClose(inner.into())),
278 },
279 Action::PositionWithdraw(inner) => pb::Action {
280 action: Some(pb::action::Action::PositionWithdraw(inner.into())),
281 },
282 Action::Ics20Withdrawal(withdrawal) => pb::Action {
283 action: Some(pb::action::Action::Ics20Withdrawal(withdrawal.into())),
284 },
285 Action::CommunityPoolSpend(inner) => pb::Action {
286 action: Some(pb::action::Action::CommunityPoolSpend(inner.into())),
287 },
288 Action::CommunityPoolOutput(inner) => pb::Action {
289 action: Some(pb::action::Action::CommunityPoolOutput(inner.into())),
290 },
291 Action::CommunityPoolDeposit(inner) => pb::Action {
292 action: Some(pb::action::Action::CommunityPoolDeposit(inner.into())),
293 },
294 Action::ActionDutchAuctionSchedule(inner) => pb::Action {
295 action: Some(pb::action::Action::ActionDutchAuctionSchedule(inner.into())),
296 },
297 Action::ActionDutchAuctionEnd(inner) => pb::Action {
298 action: Some(pb::action::Action::ActionDutchAuctionEnd(inner.into())),
299 },
300 Action::ActionDutchAuctionWithdraw(inner) => pb::Action {
301 action: Some(pb::action::Action::ActionDutchAuctionWithdraw(inner.into())),
302 },
303 }
304 }
305}
306
307impl TryFrom<pb::Action> for Action {
308 type Error = anyhow::Error;
309 fn try_from(proto: pb::Action) -> anyhow::Result<Self, Self::Error> {
310 if proto.action.is_none() {
311 anyhow::bail!("missing action content");
312 }
313 match proto
314 .action
315 .ok_or_else(|| anyhow!("missing action in Action protobuf"))?
316 {
317 pb::action::Action::Output(inner) => Ok(Action::Output(inner.try_into()?)),
318 pb::action::Action::Spend(inner) => Ok(Action::Spend(inner.try_into()?)),
319 pb::action::Action::Delegate(inner) => Ok(Action::Delegate(inner.try_into()?)),
320 pb::action::Action::Undelegate(inner) => Ok(Action::Undelegate(inner.try_into()?)),
321 pb::action::Action::UndelegateClaim(inner) => {
322 Ok(Action::UndelegateClaim(inner.try_into()?))
323 }
324 pb::action::Action::ValidatorDefinition(inner) => {
325 Ok(Action::ValidatorDefinition(inner.try_into()?))
326 }
327 pb::action::Action::SwapClaim(inner) => Ok(Action::SwapClaim(inner.try_into()?)),
328 pb::action::Action::Swap(inner) => Ok(Action::Swap(inner.try_into()?)),
329 pb::action::Action::IbcRelayAction(inner) => Ok(Action::IbcRelay(inner.try_into()?)),
330 pb::action::Action::ProposalSubmit(inner) => {
331 Ok(Action::ProposalSubmit(inner.try_into()?))
332 }
333 pb::action::Action::ProposalWithdraw(inner) => {
334 Ok(Action::ProposalWithdraw(inner.try_into()?))
335 }
336 pb::action::Action::DelegatorVote(inner) => {
337 Ok(Action::DelegatorVote(inner.try_into()?))
338 }
339 pb::action::Action::ValidatorVote(inner) => {
340 Ok(Action::ValidatorVote(inner.try_into()?))
341 }
342 pb::action::Action::ProposalDepositClaim(inner) => {
343 Ok(Action::ProposalDepositClaim(inner.try_into()?))
344 }
345
346 pb::action::Action::PositionOpen(inner) => Ok(Action::PositionOpen(inner.try_into()?)),
347 pb::action::Action::PositionClose(inner) => {
348 Ok(Action::PositionClose(inner.try_into()?))
349 }
350 pb::action::Action::PositionWithdraw(inner) => {
351 Ok(Action::PositionWithdraw(inner.try_into()?))
352 }
353 pb::action::Action::PositionRewardClaim(_) => {
354 Err(anyhow!("PositionRewardClaim is deprecated and unsupported"))
355 }
356 pb::action::Action::Ics20Withdrawal(inner) => {
357 Ok(Action::Ics20Withdrawal(inner.try_into()?))
358 }
359 pb::action::Action::CommunityPoolSpend(inner) => {
360 Ok(Action::CommunityPoolSpend(inner.try_into()?))
361 }
362 pb::action::Action::CommunityPoolOutput(inner) => {
363 Ok(Action::CommunityPoolOutput(inner.try_into()?))
364 }
365 pb::action::Action::CommunityPoolDeposit(inner) => {
366 Ok(Action::CommunityPoolDeposit(inner.try_into()?))
367 }
368 pb::action::Action::ActionDutchAuctionSchedule(inner) => {
369 Ok(Action::ActionDutchAuctionSchedule(inner.try_into()?))
370 }
371 pb::action::Action::ActionDutchAuctionEnd(inner) => {
372 Ok(Action::ActionDutchAuctionEnd(inner.try_into()?))
373 }
374 pb::action::Action::ActionDutchAuctionWithdraw(inner) => {
375 Ok(Action::ActionDutchAuctionWithdraw(inner.try_into()?))
376 }
377 }
378 }
379}