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