penumbra_sdk_transaction/plan/
action.rs

1use crate::Action;
2use crate::WitnessData;
3use anyhow::{anyhow, Context, Result};
4use ark_ff::Zero;
5use decaf377::Fr;
6use penumbra_sdk_asset::Balance;
7use penumbra_sdk_auction::auction::dutch::actions::ActionDutchAuctionEnd;
8use penumbra_sdk_auction::auction::dutch::actions::ActionDutchAuctionSchedule;
9use penumbra_sdk_auction::auction::dutch::actions::ActionDutchAuctionWithdrawPlan;
10use penumbra_sdk_community_pool::{CommunityPoolDeposit, CommunityPoolOutput, CommunityPoolSpend};
11use penumbra_sdk_txhash::{EffectHash, EffectingData};
12
13use penumbra_sdk_dex::{
14    lp::{
15        action::{PositionClose, PositionOpen},
16        plan::PositionWithdrawPlan,
17    },
18    swap::SwapPlan,
19    swap_claim::SwapClaimPlan,
20};
21use penumbra_sdk_governance::{
22    delegator_vote::DelegatorVotePlan, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw,
23    ValidatorVote,
24};
25
26use penumbra_sdk_ibc::IbcRelay;
27use penumbra_sdk_keys::{symmetric::PayloadKey, FullViewingKey};
28use penumbra_sdk_proto::{core::transaction::v1 as pb_t, DomainType};
29use penumbra_sdk_shielded_pool::{Ics20Withdrawal, OutputPlan, SpendPlan};
30use penumbra_sdk_stake::{Delegate, Undelegate, UndelegateClaimPlan};
31use serde::{Deserialize, Serialize};
32
33/// A declaration of a planned [`Action`], for use in transaction creation.
34///
35/// Actions that don't have any private data are passed through, while
36/// actions that do are replaced by a "Plan" analogue that includes
37/// openings of commitments and other private data.
38#[derive(Clone, Debug, Deserialize, Serialize)]
39#[serde(try_from = "pb_t::ActionPlan", into = "pb_t::ActionPlan")]
40#[allow(clippy::large_enum_variant)]
41pub enum ActionPlan {
42    /// Describes a proposed spend.
43    Spend(SpendPlan),
44    /// Describes a proposed output.
45    Output(OutputPlan),
46    /// We don't need any extra information (yet) to understand delegations,
47    /// because we don't yet use flow encryption.
48    Delegate(Delegate),
49    /// We don't need any extra information (yet) to understand undelegations,
50    /// because we don't yet use flow encryption.
51    Undelegate(Undelegate),
52    UndelegateClaim(UndelegateClaimPlan),
53    ValidatorDefinition(penumbra_sdk_stake::validator::Definition),
54    /// Describes a proposed swap.
55    Swap(SwapPlan),
56    /// Describes a swap claim.
57    SwapClaim(SwapClaimPlan),
58    IbcAction(IbcRelay),
59    /// Propose a governance vote.
60    ProposalSubmit(ProposalSubmit),
61    /// Withdraw a proposed vote.
62    ProposalWithdraw(ProposalWithdraw),
63    /// Vote on a proposal as a delegator.
64    DelegatorVote(DelegatorVotePlan),
65    /// Vote on a proposal as a validator.
66    ValidatorVote(ValidatorVote),
67    /// Claim the deposit for a finished proposal.
68    ProposalDepositClaim(ProposalDepositClaim),
69
70    PositionOpen(PositionOpen),
71    PositionClose(PositionClose),
72    // PositionWithdrawPlan requires the balance of the funds to be withdrawn, so
73    // a plan must be used.
74    PositionWithdraw(PositionWithdrawPlan),
75
76    CommunityPoolSpend(CommunityPoolSpend),
77    CommunityPoolOutput(CommunityPoolOutput),
78    CommunityPoolDeposit(CommunityPoolDeposit),
79
80    Ics20Withdrawal(Ics20Withdrawal),
81
82    ActionDutchAuctionSchedule(ActionDutchAuctionSchedule),
83    ActionDutchAuctionEnd(ActionDutchAuctionEnd),
84    ActionDutchAuctionWithdraw(ActionDutchAuctionWithdrawPlan),
85}
86
87impl ActionPlan {
88    /// Builds a planned [`Action`] specified by this [`ActionPlan`].
89    ///
90    /// The resulting action is `unauth` in the sense that this method does not
91    /// have access to authorization data, so any required authorization data
92    /// will be filled in with dummy zero values, to be replaced later.
93    ///
94    /// This method is useful for controlling how a transaction's actions are
95    /// built (e.g., building them in parallel, or via Web Workers).
96    pub fn build_unauth(
97        action_plan: ActionPlan,
98        fvk: &FullViewingKey,
99        witness_data: &WitnessData,
100        memo_key: Option<PayloadKey>,
101    ) -> Result<Action> {
102        use ActionPlan::*;
103
104        Ok(match action_plan {
105            Spend(spend_plan) => {
106                let note_commitment = spend_plan.note.commit();
107                let auth_path = witness_data
108                    .state_commitment_proofs
109                    .get(&note_commitment)
110                    .context(format!("could not get proof for {note_commitment:?}"))?;
111
112                Action::Spend(spend_plan.spend(
113                    fvk,
114                    [0; 64].into(),
115                    auth_path.clone(),
116                    // FIXME: why does this need the anchor? isn't that implied by the auth_path?
117                    // cf. delegator_vote
118                    witness_data.anchor,
119                ))
120            }
121            Output(output_plan) => {
122                let dummy_payload_key: PayloadKey = [0u8; 32].into();
123                Action::Output(output_plan.output(
124                    fvk.outgoing(),
125                    memo_key.as_ref().unwrap_or(&dummy_payload_key),
126                ))
127            }
128            Swap(swap_plan) => Action::Swap(swap_plan.swap(fvk)),
129            SwapClaim(swap_claim_plan) => {
130                let note_commitment = swap_claim_plan.swap_plaintext.swap_commitment();
131                let auth_path = witness_data
132                    .state_commitment_proofs
133                    .get(&note_commitment)
134                    .context(format!("could not get proof for {note_commitment:?}"))?;
135
136                Action::SwapClaim(swap_claim_plan.swap_claim(fvk, auth_path))
137            }
138            Delegate(plan) => Action::Delegate(plan.clone()),
139            Undelegate(plan) => Action::Undelegate(plan.clone()),
140            UndelegateClaim(plan) => Action::UndelegateClaim(plan.undelegate_claim()),
141            ValidatorDefinition(plan) => Action::ValidatorDefinition(plan.clone()),
142            // Fixme: action name
143            IbcAction(plan) => Action::IbcRelay(plan.clone()),
144            ProposalSubmit(plan) => Action::ProposalSubmit(plan.clone()),
145            ProposalWithdraw(plan) => Action::ProposalWithdraw(plan.clone()),
146            DelegatorVote(plan) => {
147                let note_commitment = plan.staked_note.commit();
148                let auth_path = witness_data
149                    .state_commitment_proofs
150                    .get(&note_commitment)
151                    .context(format!("could not get proof for {note_commitment:?}"))?;
152                Action::DelegatorVote(plan.delegator_vote(fvk, [0; 64].into(), auth_path.clone()))
153            }
154            ValidatorVote(plan) => Action::ValidatorVote(plan.clone()),
155            ProposalDepositClaim(plan) => Action::ProposalDepositClaim(plan.clone()),
156            PositionOpen(plan) => Action::PositionOpen(plan.clone()),
157            PositionClose(plan) => Action::PositionClose(plan.clone()),
158            PositionWithdraw(plan) => Action::PositionWithdraw(plan.position_withdraw()),
159            CommunityPoolSpend(plan) => Action::CommunityPoolSpend(plan.clone()),
160            CommunityPoolOutput(plan) => Action::CommunityPoolOutput(plan.clone()),
161            CommunityPoolDeposit(plan) => Action::CommunityPoolDeposit(plan.clone()),
162            Ics20Withdrawal(plan) => Action::Ics20Withdrawal(plan.clone()),
163            ActionDutchAuctionSchedule(plan) => Action::ActionDutchAuctionSchedule(plan.clone()),
164            ActionDutchAuctionEnd(plan) => Action::ActionDutchAuctionEnd(plan.clone()),
165            ActionDutchAuctionWithdraw(plan) => {
166                Action::ActionDutchAuctionWithdraw(plan.to_action())
167            }
168        })
169    }
170
171    /// Canonical action plan ordering according to protobuf definitions
172    pub fn variant_index(&self) -> usize {
173        match self {
174            ActionPlan::Spend(_) => 1,
175            ActionPlan::Output(_) => 2,
176            ActionPlan::Swap(_) => 3,
177            ActionPlan::SwapClaim(_) => 4,
178            ActionPlan::ValidatorDefinition(_) => 16,
179            ActionPlan::IbcAction(_) => 17,
180            ActionPlan::ProposalSubmit(_) => 18,
181            ActionPlan::ProposalWithdraw(_) => 19,
182            ActionPlan::ValidatorVote(_) => 20,
183            ActionPlan::DelegatorVote(_) => 21,
184            ActionPlan::ProposalDepositClaim(_) => 22,
185            ActionPlan::PositionOpen(_) => 30,
186            ActionPlan::PositionClose(_) => 31,
187            ActionPlan::PositionWithdraw(_) => 32,
188            ActionPlan::Delegate(_) => 40,
189            ActionPlan::Undelegate(_) => 41,
190            ActionPlan::UndelegateClaim(_) => 42,
191            ActionPlan::CommunityPoolSpend(_) => 50,
192            ActionPlan::CommunityPoolOutput(_) => 51,
193            ActionPlan::CommunityPoolDeposit(_) => 52,
194            ActionPlan::Ics20Withdrawal(_) => 200,
195            ActionPlan::ActionDutchAuctionSchedule(_) => 53,
196            ActionPlan::ActionDutchAuctionEnd(_) => 54,
197            ActionPlan::ActionDutchAuctionWithdraw(_) => 55,
198        }
199    }
200
201    pub fn balance(&self) -> Balance {
202        use ActionPlan::*;
203
204        match self {
205            Spend(spend) => spend.balance(),
206            Output(output) => output.balance(),
207            Delegate(delegate) => delegate.balance(),
208            Undelegate(undelegate) => undelegate.balance(),
209            UndelegateClaim(undelegate_claim) => undelegate_claim.balance(),
210            Swap(swap) => swap.balance(),
211            SwapClaim(swap_claim) => swap_claim.balance(),
212            ProposalSubmit(proposal_submit) => proposal_submit.balance(),
213            ProposalWithdraw(proposal_withdraw) => proposal_withdraw.balance(),
214            ProposalDepositClaim(proposal_deposit_claim) => proposal_deposit_claim.balance(),
215            DelegatorVote(delegator_vote) => delegator_vote.balance(),
216            CommunityPoolSpend(community_pool_spend) => community_pool_spend.balance(),
217            CommunityPoolOutput(community_pool_output) => community_pool_output.balance(),
218            CommunityPoolDeposit(community_pool_deposit) => community_pool_deposit.balance(),
219            PositionOpen(position_open) => position_open.balance(),
220            PositionClose(position_close) => position_close.balance(),
221            PositionWithdraw(position_withdraw) => position_withdraw.balance(),
222            Ics20Withdrawal(withdrawal) => withdrawal.balance(),
223            ActionDutchAuctionSchedule(action) => action.balance(),
224            ActionDutchAuctionEnd(action) => action.balance(),
225            ActionDutchAuctionWithdraw(action) => action.balance(),
226
227            // None of these contribute to transaction balance:
228            IbcAction(_) | ValidatorDefinition(_) | ValidatorVote(_) => Balance::default(),
229        }
230    }
231
232    pub fn value_blinding(&self) -> Fr {
233        use ActionPlan::*;
234
235        match self {
236            Spend(spend) => spend.value_blinding,
237            Output(output) => output.value_blinding,
238            Delegate(_) => Fr::zero(),
239            Undelegate(_) => Fr::zero(),
240            UndelegateClaim(undelegate_claim) => undelegate_claim.balance_blinding,
241            ValidatorDefinition(_) => Fr::zero(),
242            Swap(swap) => swap.fee_blinding,
243            SwapClaim(_) => Fr::zero(),
244            IbcAction(_) => Fr::zero(),
245            ProposalSubmit(_) => Fr::zero(),
246            ProposalWithdraw(_) => Fr::zero(),
247            DelegatorVote(_) => Fr::zero(),
248            ValidatorVote(_) => Fr::zero(),
249            ProposalDepositClaim(_) => Fr::zero(),
250            PositionOpen(_) => Fr::zero(),
251            PositionClose(_) => Fr::zero(),
252            PositionWithdraw(_) => Fr::zero(),
253            CommunityPoolSpend(_) => Fr::zero(),
254            CommunityPoolOutput(_) => Fr::zero(),
255            CommunityPoolDeposit(_) => Fr::zero(),
256            Ics20Withdrawal(_) => Fr::zero(),
257            ActionDutchAuctionSchedule(_) => Fr::zero(),
258            ActionDutchAuctionEnd(_) => Fr::zero(),
259            ActionDutchAuctionWithdraw(_) => Fr::zero(),
260        }
261    }
262
263    /// Compute the effect hash of the action this plan will produce.
264    pub fn effect_hash(&self, fvk: &FullViewingKey, memo_key: &PayloadKey) -> EffectHash {
265        use ActionPlan::*;
266
267        match self {
268            Spend(plan) => plan.spend_body(fvk).effect_hash(),
269            Output(plan) => plan.output_body(fvk.outgoing(), memo_key).effect_hash(),
270            Delegate(plan) => plan.effect_hash(),
271            Undelegate(plan) => plan.effect_hash(),
272            UndelegateClaim(plan) => plan.undelegate_claim_body().effect_hash(),
273            ValidatorDefinition(plan) => plan.effect_hash(),
274            Swap(plan) => plan.swap_body(fvk).effect_hash(),
275            SwapClaim(plan) => plan.swap_claim_body(fvk).effect_hash(),
276            IbcAction(plan) => plan.effect_hash(),
277            ProposalSubmit(plan) => plan.effect_hash(),
278            ProposalWithdraw(plan) => plan.effect_hash(),
279            DelegatorVote(plan) => plan.delegator_vote_body(fvk).effect_hash(),
280            ValidatorVote(plan) => plan.effect_hash(),
281            ProposalDepositClaim(plan) => plan.effect_hash(),
282            PositionOpen(plan) => plan.effect_hash(),
283            PositionClose(plan) => plan.effect_hash(),
284            PositionWithdraw(plan) => plan.position_withdraw().effect_hash(),
285            CommunityPoolSpend(plan) => plan.effect_hash(),
286            CommunityPoolOutput(plan) => plan.effect_hash(),
287            CommunityPoolDeposit(plan) => plan.effect_hash(),
288            Ics20Withdrawal(plan) => plan.effect_hash(),
289            ActionDutchAuctionSchedule(plan) => plan.effect_hash(),
290            ActionDutchAuctionEnd(plan) => plan.effect_hash(),
291            ActionDutchAuctionWithdraw(plan) => plan.to_action().effect_hash(),
292        }
293    }
294}
295
296// Convenience impls that make declarative transaction construction easier.
297
298impl From<SpendPlan> for ActionPlan {
299    fn from(inner: SpendPlan) -> ActionPlan {
300        ActionPlan::Spend(inner)
301    }
302}
303
304impl From<OutputPlan> for ActionPlan {
305    fn from(inner: OutputPlan) -> ActionPlan {
306        ActionPlan::Output(inner)
307    }
308}
309
310impl From<SwapPlan> for ActionPlan {
311    fn from(inner: SwapPlan) -> ActionPlan {
312        ActionPlan::Swap(inner)
313    }
314}
315
316impl From<SwapClaimPlan> for ActionPlan {
317    fn from(inner: SwapClaimPlan) -> ActionPlan {
318        ActionPlan::SwapClaim(inner)
319    }
320}
321
322impl From<Delegate> for ActionPlan {
323    fn from(inner: Delegate) -> ActionPlan {
324        ActionPlan::Delegate(inner)
325    }
326}
327
328impl From<Undelegate> for ActionPlan {
329    fn from(inner: Undelegate) -> ActionPlan {
330        ActionPlan::Undelegate(inner)
331    }
332}
333
334impl From<UndelegateClaimPlan> for ActionPlan {
335    fn from(inner: UndelegateClaimPlan) -> ActionPlan {
336        ActionPlan::UndelegateClaim(inner)
337    }
338}
339
340impl From<penumbra_sdk_stake::validator::Definition> for ActionPlan {
341    fn from(inner: penumbra_sdk_stake::validator::Definition) -> ActionPlan {
342        ActionPlan::ValidatorDefinition(inner)
343    }
344}
345
346impl From<IbcRelay> for ActionPlan {
347    fn from(inner: IbcRelay) -> ActionPlan {
348        ActionPlan::IbcAction(inner)
349    }
350}
351
352impl From<ProposalSubmit> for ActionPlan {
353    fn from(inner: ProposalSubmit) -> ActionPlan {
354        ActionPlan::ProposalSubmit(inner)
355    }
356}
357
358impl From<DelegatorVotePlan> for ActionPlan {
359    fn from(inner: DelegatorVotePlan) -> ActionPlan {
360        ActionPlan::DelegatorVote(inner)
361    }
362}
363
364impl From<ValidatorVote> for ActionPlan {
365    fn from(inner: ValidatorVote) -> ActionPlan {
366        ActionPlan::ValidatorVote(inner)
367    }
368}
369
370impl From<PositionOpen> for ActionPlan {
371    fn from(inner: PositionOpen) -> ActionPlan {
372        ActionPlan::PositionOpen(inner)
373    }
374}
375
376impl From<PositionClose> for ActionPlan {
377    fn from(inner: PositionClose) -> ActionPlan {
378        ActionPlan::PositionClose(inner)
379    }
380}
381
382impl From<PositionWithdrawPlan> for ActionPlan {
383    fn from(inner: PositionWithdrawPlan) -> ActionPlan {
384        ActionPlan::PositionWithdraw(inner)
385    }
386}
387
388impl From<CommunityPoolSpend> for ActionPlan {
389    fn from(inner: CommunityPoolSpend) -> ActionPlan {
390        ActionPlan::CommunityPoolSpend(inner)
391    }
392}
393
394impl From<CommunityPoolOutput> for ActionPlan {
395    fn from(inner: CommunityPoolOutput) -> ActionPlan {
396        ActionPlan::CommunityPoolOutput(inner)
397    }
398}
399
400impl From<CommunityPoolDeposit> for ActionPlan {
401    fn from(inner: CommunityPoolDeposit) -> ActionPlan {
402        ActionPlan::CommunityPoolDeposit(inner)
403    }
404}
405
406impl From<Ics20Withdrawal> for ActionPlan {
407    fn from(inner: Ics20Withdrawal) -> ActionPlan {
408        ActionPlan::Ics20Withdrawal(inner)
409    }
410}
411
412impl From<ActionDutchAuctionSchedule> for ActionPlan {
413    fn from(inner: ActionDutchAuctionSchedule) -> ActionPlan {
414        ActionPlan::ActionDutchAuctionSchedule(inner)
415    }
416}
417
418impl From<ActionDutchAuctionEnd> for ActionPlan {
419    fn from(inner: ActionDutchAuctionEnd) -> ActionPlan {
420        ActionPlan::ActionDutchAuctionEnd(inner)
421    }
422}
423
424impl From<ActionDutchAuctionWithdrawPlan> for ActionPlan {
425    fn from(inner: ActionDutchAuctionWithdrawPlan) -> ActionPlan {
426        ActionPlan::ActionDutchAuctionWithdraw(inner)
427    }
428}
429
430impl From<ProposalWithdraw> for ActionPlan {
431    fn from(inner: ProposalWithdraw) -> ActionPlan {
432        ActionPlan::ProposalWithdraw(inner)
433    }
434}
435
436impl From<ProposalDepositClaim> for ActionPlan {
437    fn from(inner: ProposalDepositClaim) -> ActionPlan {
438        ActionPlan::ProposalDepositClaim(inner)
439    }
440}
441
442impl DomainType for ActionPlan {
443    type Proto = pb_t::ActionPlan;
444}
445
446impl From<ActionPlan> for pb_t::ActionPlan {
447    fn from(msg: ActionPlan) -> Self {
448        match msg {
449            ActionPlan::Output(inner) => pb_t::ActionPlan {
450                action: Some(pb_t::action_plan::Action::Output(inner.into())),
451            },
452            ActionPlan::Spend(inner) => pb_t::ActionPlan {
453                action: Some(pb_t::action_plan::Action::Spend(inner.into())),
454            },
455            ActionPlan::Delegate(inner) => pb_t::ActionPlan {
456                action: Some(pb_t::action_plan::Action::Delegate(inner.into())),
457            },
458            ActionPlan::Undelegate(inner) => pb_t::ActionPlan {
459                action: Some(pb_t::action_plan::Action::Undelegate(inner.into())),
460            },
461            ActionPlan::UndelegateClaim(inner) => pb_t::ActionPlan {
462                action: Some(pb_t::action_plan::Action::UndelegateClaim(inner.into())),
463            },
464            ActionPlan::ValidatorDefinition(inner) => pb_t::ActionPlan {
465                action: Some(pb_t::action_plan::Action::ValidatorDefinition(inner.into())),
466            },
467            ActionPlan::SwapClaim(inner) => pb_t::ActionPlan {
468                action: Some(pb_t::action_plan::Action::SwapClaim(inner.into())),
469            },
470            ActionPlan::Swap(inner) => pb_t::ActionPlan {
471                action: Some(pb_t::action_plan::Action::Swap(inner.into())),
472            },
473            ActionPlan::IbcAction(inner) => pb_t::ActionPlan {
474                action: Some(pb_t::action_plan::Action::IbcRelayAction(inner.into())),
475            },
476            ActionPlan::ProposalSubmit(inner) => pb_t::ActionPlan {
477                action: Some(pb_t::action_plan::Action::ProposalSubmit(inner.into())),
478            },
479            ActionPlan::ProposalWithdraw(inner) => pb_t::ActionPlan {
480                action: Some(pb_t::action_plan::Action::ProposalWithdraw(inner.into())),
481            },
482            ActionPlan::DelegatorVote(inner) => pb_t::ActionPlan {
483                action: Some(pb_t::action_plan::Action::DelegatorVote(inner.into())),
484            },
485            ActionPlan::ValidatorVote(inner) => pb_t::ActionPlan {
486                action: Some(pb_t::action_plan::Action::ValidatorVote(inner.into())),
487            },
488            ActionPlan::ProposalDepositClaim(inner) => pb_t::ActionPlan {
489                action: Some(pb_t::action_plan::Action::ProposalDepositClaim(
490                    inner.into(),
491                )),
492            },
493            ActionPlan::PositionOpen(inner) => pb_t::ActionPlan {
494                action: Some(pb_t::action_plan::Action::PositionOpen(inner.into())),
495            },
496            ActionPlan::PositionClose(inner) => pb_t::ActionPlan {
497                action: Some(pb_t::action_plan::Action::PositionClose(inner.into())),
498            },
499            ActionPlan::PositionWithdraw(inner) => pb_t::ActionPlan {
500                action: Some(pb_t::action_plan::Action::PositionWithdraw(Into::<
501                    penumbra_sdk_proto::core::component::dex::v1::PositionWithdrawPlan,
502                >::into(
503                    inner
504                ))),
505            },
506            ActionPlan::CommunityPoolDeposit(inner) => pb_t::ActionPlan {
507                action: Some(pb_t::action_plan::Action::CommunityPoolDeposit(
508                    inner.into(),
509                )),
510            },
511            ActionPlan::CommunityPoolSpend(inner) => pb_t::ActionPlan {
512                action: Some(pb_t::action_plan::Action::CommunityPoolSpend(inner.into())),
513            },
514            ActionPlan::CommunityPoolOutput(inner) => pb_t::ActionPlan {
515                action: Some(pb_t::action_plan::Action::CommunityPoolOutput(inner.into())),
516            },
517            ActionPlan::Ics20Withdrawal(inner) => pb_t::ActionPlan {
518                action: Some(pb_t::action_plan::Action::Ics20Withdrawal(inner.into())),
519            },
520            ActionPlan::ActionDutchAuctionSchedule(inner) => pb_t::ActionPlan {
521                action: Some(pb_t::action_plan::Action::ActionDutchAuctionSchedule(
522                    inner.into(),
523                )),
524            },
525            ActionPlan::ActionDutchAuctionEnd(inner) => pb_t::ActionPlan {
526                action: Some(pb_t::action_plan::Action::ActionDutchAuctionEnd(
527                    inner.into(),
528                )),
529            },
530            ActionPlan::ActionDutchAuctionWithdraw(inner) => pb_t::ActionPlan {
531                action: Some(pb_t::action_plan::Action::ActionDutchAuctionWithdraw(
532                    inner.into(),
533                )),
534            },
535        }
536    }
537}
538
539impl TryFrom<pb_t::ActionPlan> for ActionPlan {
540    type Error = anyhow::Error;
541
542    fn try_from(proto: pb_t::ActionPlan) -> anyhow::Result<Self, Self::Error> {
543        if proto.action.is_none() {
544            anyhow::bail!("missing action content");
545        }
546
547        match proto
548            .action
549            .ok_or_else(|| anyhow!("missing action in ActionPlan proto"))?
550        {
551            pb_t::action_plan::Action::Output(inner) => Ok(ActionPlan::Output(inner.try_into()?)),
552            pb_t::action_plan::Action::Spend(inner) => Ok(ActionPlan::Spend(inner.try_into()?)),
553            pb_t::action_plan::Action::Delegate(inner) => {
554                Ok(ActionPlan::Delegate(inner.try_into()?))
555            }
556            pb_t::action_plan::Action::Undelegate(inner) => {
557                Ok(ActionPlan::Undelegate(inner.try_into()?))
558            }
559            pb_t::action_plan::Action::UndelegateClaim(inner) => {
560                Ok(ActionPlan::UndelegateClaim(inner.try_into()?))
561            }
562            pb_t::action_plan::Action::ValidatorDefinition(inner) => {
563                Ok(ActionPlan::ValidatorDefinition(inner.try_into()?))
564            }
565            pb_t::action_plan::Action::Swap(inner) => Ok(ActionPlan::Swap(inner.try_into()?)),
566            pb_t::action_plan::Action::SwapClaim(inner) => {
567                Ok(ActionPlan::SwapClaim(inner.try_into()?))
568            }
569            pb_t::action_plan::Action::IbcRelayAction(inner) => {
570                Ok(ActionPlan::IbcAction(inner.try_into()?))
571            }
572            pb_t::action_plan::Action::ProposalSubmit(inner) => {
573                Ok(ActionPlan::ProposalSubmit(inner.try_into()?))
574            }
575            pb_t::action_plan::Action::ProposalWithdraw(inner) => {
576                Ok(ActionPlan::ProposalWithdraw(inner.try_into()?))
577            }
578            pb_t::action_plan::Action::ValidatorVote(inner) => {
579                Ok(ActionPlan::ValidatorVote(inner.try_into()?))
580            }
581            pb_t::action_plan::Action::DelegatorVote(inner) => {
582                Ok(ActionPlan::DelegatorVote(inner.try_into()?))
583            }
584            pb_t::action_plan::Action::ProposalDepositClaim(inner) => {
585                Ok(ActionPlan::ProposalDepositClaim(inner.try_into()?))
586            }
587            pb_t::action_plan::Action::PositionOpen(inner) => {
588                Ok(ActionPlan::PositionOpen(inner.try_into()?))
589            }
590            pb_t::action_plan::Action::PositionClose(inner) => {
591                Ok(ActionPlan::PositionClose(inner.try_into()?))
592            }
593            pb_t::action_plan::Action::PositionWithdraw(inner) => {
594                Ok(ActionPlan::PositionWithdraw(inner.try_into()?))
595            }
596            pb_t::action_plan::Action::PositionRewardClaim(_) => {
597                Err(anyhow!("PositionRewardClaim is deprecated and unsupported"))
598            }
599            pb_t::action_plan::Action::CommunityPoolSpend(inner) => {
600                Ok(ActionPlan::CommunityPoolSpend(inner.try_into()?))
601            }
602            pb_t::action_plan::Action::CommunityPoolDeposit(inner) => {
603                Ok(ActionPlan::CommunityPoolDeposit(inner.try_into()?))
604            }
605            pb_t::action_plan::Action::CommunityPoolOutput(inner) => {
606                Ok(ActionPlan::CommunityPoolOutput(inner.try_into()?))
607            }
608            pb_t::action_plan::Action::ActionDutchAuctionSchedule(inner) => {
609                Ok(ActionPlan::ActionDutchAuctionSchedule(inner.try_into()?))
610            }
611            pb_t::action_plan::Action::ActionDutchAuctionEnd(inner) => {
612                Ok(ActionPlan::ActionDutchAuctionEnd(inner.try_into()?))
613            }
614            pb_t::action_plan::Action::ActionDutchAuctionWithdraw(inner) => {
615                Ok(ActionPlan::ActionDutchAuctionWithdraw(inner.try_into()?))
616            }
617            pb_t::action_plan::Action::Ics20Withdrawal(inner) => {
618                Ok(ActionPlan::Ics20Withdrawal(inner.try_into()?))
619            }
620        }
621    }
622}