use indexmap::{IndexMap, map::Entry};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default)]
pub struct FeeReceiptBuilder {
pub total_fee_payment: u64,
pub total_fees_paid: u64,
pub total_fee_overcharge: u64,
pub cost_breakdown: FeeBreakdown,
}
impl FeeReceiptBuilder {
pub fn with_total_fee_payment(mut self, amount: u64) -> Self {
self.total_fee_payment = amount;
self
}
pub fn with_total_fees_paid(mut self, amount: u64) -> Self {
self.total_fees_paid = amount;
self
}
pub fn with_total_fee_overcharge(mut self, amount: u64) -> Self {
self.total_fee_overcharge = amount;
self
}
pub fn with_cost_breakdown(mut self, breakdown: FeeBreakdown) -> Self {
self.cost_breakdown = breakdown;
self
}
pub fn build(self) -> FeeReceipt {
FeeReceipt {
total_fee_payment: self.total_fee_payment,
total_fees_paid: self.total_fees_paid,
total_fee_overcharge: self.total_fee_overcharge,
cost_breakdown: self.cost_breakdown,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, borsh::BorshSerialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub struct FeeReceipt {
total_fee_payment: u64,
total_fees_paid: u64,
total_fee_overcharge: u64,
cost_breakdown: FeeBreakdown,
}
impl FeeReceipt {
pub fn to_cost_breakdown(&self) -> FeeCostBreakdown {
FeeCostBreakdown {
total_fees_charged: self.total_fees_charged(),
required_fees: self.required_fees(),
breakdown: self.cost_breakdown.clone(),
}
}
pub fn fee_breakdown(&self) -> &FeeBreakdown {
&self.cost_breakdown
}
pub fn total_fees_charged(&self) -> u64 {
self.cost_breakdown.get_total()
}
pub fn required_fees(&self) -> u64 {
self.total_fees_charged().saturating_add(1)
}
pub fn total_refunded(&self) -> u64 {
self.total_fee_payment
.checked_sub(self.total_fees_charged())
.and_then(|v| v.checked_sub(self.total_fee_overcharge))
.unwrap_or_default()
}
pub fn total_allocated_fee_payments(&self) -> u64 {
self.total_fee_payment
}
pub fn total_fees_paid(&self) -> u64 {
self.total_fees_paid
}
pub fn total_fee_payment(&self) -> u64 {
self.total_fee_payment
}
pub fn unpaid_debt(&self) -> u64 {
self.total_fees_charged().saturating_sub(self.total_fees_paid())
}
pub fn is_paid_in_full(&self) -> bool {
self.unpaid_debt() == 0
}
pub fn total_fee_overcharge(&self) -> u64 {
self.total_fee_overcharge
}
}
impl Default for FeeReceipt {
fn default() -> Self {
FeeReceiptBuilder::default().build()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, borsh::BorshSerialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub enum FeeSource {
Initial,
RuntimeCall,
Storage,
Events,
Logs,
TransactionWeight,
SignatureVerification,
TemplateLoad,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, borsh::BorshSerialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub struct FeeBreakdown {
breakdown: IndexMap<FeeSource, u64>,
}
impl FeeBreakdown {
pub fn add(&mut self, source: FeeSource, amount: u64) {
match self.breakdown.entry(source) {
Entry::Occupied(entry) => {
*entry.into_mut() += amount;
},
Entry::Vacant(entry) => {
entry.insert(amount);
self.breakdown.sort_keys();
},
}
}
pub fn iter(&self) -> impl Iterator<Item = (&FeeSource, &u64)> {
self.breakdown.iter()
}
pub fn get_total(&self) -> u64 {
self.breakdown.values().sum()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub struct FeeCostBreakdown {
pub total_fees_charged: u64,
pub required_fees: u64,
pub breakdown: FeeBreakdown,
}