penumbra_fee/component/
fee_pay.rsuse anyhow::{ensure, Result};
use async_trait::async_trait;
use cnidarium::StateWrite;
use penumbra_asset::Value;
use penumbra_proto::core::component::fee::v1 as pb;
use penumbra_proto::state::StateWriteProto as _;
use crate::{Fee, Gas};
use super::view::{StateReadExt, StateWriteExt};
#[async_trait]
pub trait FeePay: StateWrite {
async fn pay_fee(&mut self, gas_used: Gas, fee: Fee) -> Result<()> {
let current_gas_prices = if fee.asset_id() == *penumbra_asset::STAKING_TOKEN_ASSET_ID {
self.get_gas_prices()
.await
.expect("gas prices must be present in state")
} else {
let alt_gas_prices = self
.get_alt_gas_prices()
.await
.expect("alt gas prices must be present in state");
alt_gas_prices
.into_iter()
.find(|prices| prices.asset_id == fee.asset_id())
.ok_or_else(|| {
anyhow::anyhow!("fee token {} not recognized by the chain", fee.asset_id())
})?
};
ensure!(
current_gas_prices.asset_id == fee.asset_id(),
"unexpected mismatch between fee and queried gas prices (expected: {}, found: {})",
fee.asset_id(),
current_gas_prices.asset_id,
);
let base_fee = current_gas_prices.fee(&gas_used);
ensure!(
fee.amount() >= base_fee.amount(),
"fee must be greater than or equal to the transaction base price (supplied: {}, base: {})",
fee.amount(),
base_fee.amount(),
);
let tip = Fee(Value {
amount: fee.amount() - base_fee.amount(),
asset_id: fee.asset_id(),
});
self.record_proto(pb::EventPaidFee {
fee: Some(fee.into()),
base_fee: Some(base_fee.into()),
gas_used: Some(gas_used.into()),
tip: Some(tip.into()),
});
self.raw_accumulate_base_fee(base_fee);
self.raw_accumulate_tip(tip);
Ok(())
}
}
impl<S: StateWrite + ?Sized> FeePay for S {}