penumbra_sdk_fee/component/
view.rs

1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3use cnidarium::{StateRead, StateWrite};
4use penumbra_sdk_asset::asset;
5use penumbra_sdk_num::Amount;
6use penumbra_sdk_proto::{StateReadProto, StateWriteProto};
7
8use crate::{params::FeeParameters, state_key, Fee, GasPrices};
9
10/// This trait provides read access to fee-related parts of the Penumbra
11/// state store.
12#[async_trait]
13pub trait StateReadExt: StateRead {
14    /// Gets the fee parameters from the JMT.
15    async fn get_fee_params(&self) -> Result<FeeParameters> {
16        self.get(state_key::fee_params())
17            .await?
18            .ok_or_else(|| anyhow!("Missing FeeParameters"))
19    }
20
21    /// Gets the current gas prices for the fee token.
22    async fn get_gas_prices(&self) -> Result<GasPrices> {
23        // When we implement dynamic gas pricing, we will want
24        // to read the prices we computed. But until then, we need to
25        // read these from the _fee params_ instead, since those are
26        // the values that will get updated by governance.
27        let params = self.get_fee_params().await?;
28        Ok(params.fixed_gas_prices)
29    }
30
31    /// Gets the current gas prices for alternative fee tokens.
32    async fn get_alt_gas_prices(&self) -> Result<Vec<GasPrices>> {
33        // When we implement dynamic gas pricing, we will want
34        // to read the prices we computed. But until then, we need to
35        // read these from the _fee params_ instead, since those are
36        // the values that will get updated by governance.
37        let params = self.get_fee_params().await?;
38        Ok(params.fixed_alt_gas_prices)
39    }
40
41    /// Returns true if the gas prices have been changed in this block.
42    fn gas_prices_changed(&self) -> bool {
43        self.object_get::<()>(state_key::gas_prices_changed())
44            .is_some()
45    }
46
47    /// The accumulated base fees and tips for this block, indexed by asset ID.
48    fn accumulated_base_fees_and_tips(&self) -> im::OrdMap<asset::Id, (Amount, Amount)> {
49        self.object_get(state_key::fee_accumulator())
50            .unwrap_or_default()
51    }
52}
53
54impl<T: StateRead + ?Sized> StateReadExt for T {}
55
56#[async_trait]
57pub trait StateWriteExt: StateWrite {
58    /// Writes the provided fee parameters to the JMT.
59    fn put_fee_params(&mut self, params: FeeParameters) {
60        self.put(state_key::fee_params().into(), params);
61        // This could have changed the gas prices, so mark them as changed.
62        self.object_put(state_key::gas_prices_changed(), ());
63    }
64
65    /*
66    We shouldn't be setting gas prices directly, until we have dynamic gas pricing.
67    /// Writes the provided gas prices to the JMT.
68    fn put_gas_prices(&mut self, gas_prices: GasPrices) {
69        // Change the gas prices:
70        self.put(state_key::gas_prices().into(), gas_prices);
71
72        // Mark that they've changed
73        self.object_put(state_key::gas_prices_changed(), ());
74    }
75     */
76
77    /// Takes the accumulated base fees and tips for this block, resetting them to zero.
78    fn take_accumulated_base_fees_and_tips(&mut self) -> im::OrdMap<asset::Id, (Amount, Amount)> {
79        let old = self.accumulated_base_fees_and_tips();
80        let new = im::OrdMap::<asset::Id, (Amount, Amount)>::new();
81        self.object_put(state_key::fee_accumulator(), new);
82        old
83    }
84
85    fn raw_accumulate_base_fee(&mut self, base_fee: Fee) {
86        let old = self.accumulated_base_fees_and_tips();
87        let new = old.alter(
88            |maybe_amounts| match maybe_amounts {
89                Some((base, tip)) => Some((base + base_fee.amount(), tip)),
90                None => Some((base_fee.amount(), Amount::zero())),
91            },
92            base_fee.asset_id(),
93        );
94        self.object_put(state_key::fee_accumulator(), new);
95    }
96
97    fn raw_accumulate_tip(&mut self, tip_fee: Fee) {
98        let old = self.accumulated_base_fees_and_tips();
99        let new = old.alter(
100            |maybe_amounts| match maybe_amounts {
101                Some((base, tip)) => Some((base, tip + tip_fee.amount())),
102                None => Some((Amount::zero(), tip_fee.amount())),
103            },
104            tip_fee.asset_id(),
105        );
106        self.object_put(state_key::fee_accumulator(), new);
107    }
108}
109
110impl<T: StateWrite + ?Sized> StateWriteExt for T {}