penumbra_sdk_dex/component/action_handler/
swap.rs

1use std::sync::Arc;
2
3use anyhow::{ensure, Result};
4use async_trait::async_trait;
5use cnidarium::StateWrite;
6use cnidarium_component::ActionHandler;
7use penumbra_sdk_proof_params::SWAP_PROOF_VERIFICATION_KEY;
8use penumbra_sdk_proto::{DomainType as _, StateWriteProto};
9use penumbra_sdk_sct::component::source::SourceContext;
10
11use crate::{
12    component::{InternalDexWrite, StateReadExt, SwapDataWrite, SwapManager},
13    event,
14    swap::{proof::SwapProofPublic, Swap},
15};
16
17#[async_trait]
18impl ActionHandler for Swap {
19    type CheckStatelessContext = ();
20    async fn check_stateless(&self, _context: ()) -> Result<()> {
21        // Check that the trading pair is distinct.
22        if self.body.trading_pair.asset_1() == self.body.trading_pair.asset_2() {
23            anyhow::bail!("Trading pair must be distinct");
24        }
25
26        self.proof.verify(
27            &SWAP_PROOF_VERIFICATION_KEY,
28            SwapProofPublic {
29                balance_commitment: self.balance_commitment_inner(),
30                swap_commitment: self.body.payload.commitment,
31                fee_commitment: self.body.fee_commitment,
32            },
33        )?;
34
35        Ok(())
36    }
37
38    async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
39        // Only execute the swap if the dex is enabled in the dex params.
40        let dex_params = state.get_dex_params().await?;
41
42        ensure!(
43            dex_params.is_enabled,
44            "Dex MUST be enabled to process swap actions."
45        );
46
47        let swap = self;
48
49        // Accumulate the swap's flows, crediting the DEX VCB for the inflows.
50        let flow = (swap.body.delta_1_i, swap.body.delta_2_i);
51        state
52            .accumulate_swap_flow(&swap.body.trading_pair, flow.into())
53            .await?;
54
55        // Record the swap commitment in the state.
56        let source = state.get_current_source().expect("source is set");
57        state
58            .add_swap_payload(self.body.payload.clone(), source)
59            .await;
60
61        // Mark the assets for the swap's trading pair as accessed during this block.
62        let fixed_candidates = Arc::new(dex_params.fixed_candidates.clone());
63        state.add_recently_accessed_asset(
64            swap.body.trading_pair.asset_1(),
65            fixed_candidates.clone(),
66        );
67        state.add_recently_accessed_asset(swap.body.trading_pair.asset_2(), fixed_candidates);
68
69        state.record_proto(event::EventSwap::from(self).to_proto());
70
71        Ok(())
72    }
73}