use fraction::Fraction;
use hiero_sdk_proto::services;
use crate::{
AccountId,
FromProtobuf,
Hbar,
ToProtobuf,
TokenId,
};
#[cfg(test)]
mod tests;
pub type AnyCustomFee = CustomFee<Fee>;
pub type FixedFee = CustomFee<FixedFeeData>;
pub type FractionalFee = CustomFee<FractionalFeeData>;
pub type RoyaltyFee = CustomFee<RoyaltyFeeData>;
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct CustomFee<Fee> {
pub fee: Fee,
pub fee_collector_account_id: Option<AccountId>,
pub all_collectors_are_exempt: bool,
}
impl AnyCustomFee {
pub fn from_bytes(bytes: &[u8]) -> crate::Result<Self> {
FromProtobuf::from_bytes(bytes)
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
ToProtobuf::to_bytes(self)
}
}
impl FromProtobuf<services::CustomFee> for AnyCustomFee {
fn from_protobuf(pb: services::CustomFee) -> crate::Result<Self>
where
Self: Sized,
{
let fee_collector_account_id = Option::from_protobuf(pb.fee_collector_account_id)?;
let fee = pb_getf!(pb, fee)?;
let fee: Fee = match fee {
services::custom_fee::Fee::FixedFee(pb) => FixedFeeData::from_protobuf(pb)?.into(),
services::custom_fee::Fee::FractionalFee(pb) => {
FractionalFeeData::from_protobuf(pb)?.into()
}
services::custom_fee::Fee::RoyaltyFee(pb) => RoyaltyFeeData::from_protobuf(pb)?.into(),
};
Ok(Self {
fee,
fee_collector_account_id,
all_collectors_are_exempt: pb.all_collectors_are_exempt,
})
}
}
impl ToProtobuf for AnyCustomFee {
type Protobuf = services::CustomFee;
fn to_protobuf(&self) -> Self::Protobuf {
services::CustomFee {
fee_collector_account_id: self.fee_collector_account_id.to_protobuf(),
fee: Some(self.fee.to_protobuf()),
all_collectors_are_exempt: self.all_collectors_are_exempt,
}
}
}
impl From<FixedFee> for AnyCustomFee {
fn from(v: FixedFee) -> Self {
Self {
fee: v.fee.into(),
fee_collector_account_id: v.fee_collector_account_id,
all_collectors_are_exempt: v.all_collectors_are_exempt,
}
}
}
impl From<FractionalFee> for AnyCustomFee {
fn from(v: FractionalFee) -> Self {
Self {
fee: v.fee.into(),
fee_collector_account_id: v.fee_collector_account_id,
all_collectors_are_exempt: v.all_collectors_are_exempt,
}
}
}
impl From<RoyaltyFee> for AnyCustomFee {
fn from(v: RoyaltyFee) -> Self {
Self {
fee: v.fee.into(),
fee_collector_account_id: v.fee_collector_account_id,
all_collectors_are_exempt: v.all_collectors_are_exempt,
}
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum Fee {
Fixed(FixedFeeData),
Fractional(FractionalFeeData),
Royalty(RoyaltyFeeData),
}
impl FromProtobuf<services::custom_fee::Fee> for Fee {
fn from_protobuf(pb: services::custom_fee::Fee) -> crate::Result<Self>
where
Self: Sized,
{
use services::custom_fee::Fee as ProtoFee;
match pb {
ProtoFee::FixedFee(it) => Ok(FixedFeeData::from_protobuf(it)?.into()),
ProtoFee::FractionalFee(it) => Ok(FractionalFeeData::from_protobuf(it)?.into()),
ProtoFee::RoyaltyFee(it) => Ok(RoyaltyFeeData::from_protobuf(it)?.into()),
}
}
}
impl ToProtobuf for Fee {
type Protobuf = services::custom_fee::Fee;
fn to_protobuf(&self) -> Self::Protobuf {
use services::custom_fee::Fee as ProtoFee;
match self {
Self::Fixed(it) => ProtoFee::FixedFee(it.to_protobuf()),
Self::Fractional(it) => ProtoFee::FractionalFee(it.to_protobuf()),
Self::Royalty(it) => ProtoFee::RoyaltyFee(it.to_protobuf()),
}
}
}
impl From<FixedFeeData> for Fee {
fn from(v: FixedFeeData) -> Self {
Self::Fixed(v)
}
}
impl From<FractionalFeeData> for Fee {
fn from(v: FractionalFeeData) -> Self {
Self::Fractional(v)
}
}
impl From<RoyaltyFeeData> for Fee {
fn from(v: RoyaltyFeeData) -> Self {
Self::Royalty(v)
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct FixedFeeData {
pub amount: i64,
pub denominating_token_id: Option<TokenId>,
}
impl FixedFeeData {
#[must_use]
pub fn from_hbar(amount: Hbar) -> Self {
Self { amount: amount.to_tinybars(), denominating_token_id: None }
}
#[must_use]
pub fn get_hbar(&self) -> Option<Hbar> {
self.denominating_token_id.is_none().then(|| Hbar::from_tinybars(self.amount))
}
}
impl FromProtobuf<services::FixedFee> for FixedFeeData {
fn from_protobuf(pb: services::FixedFee) -> crate::Result<Self> {
Ok(Self {
amount: pb.amount,
denominating_token_id: Option::from_protobuf(pb.denominating_token_id)?,
})
}
}
impl ToProtobuf for FixedFeeData {
type Protobuf = services::FixedFee;
fn to_protobuf(&self) -> Self::Protobuf {
Self::Protobuf {
amount: self.amount,
denominating_token_id: self.denominating_token_id.to_protobuf(),
}
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct FractionalFeeData {
pub denominator: u64,
pub numerator: u64,
pub minimum_amount: i64,
pub maximum_amount: i64,
pub assessment_method: FeeAssessmentMethod,
}
impl FromProtobuf<services::FractionalFee> for FractionalFeeData {
fn from_protobuf(pb: services::FractionalFee) -> crate::Result<Self> {
let amount = pb.fractional_amount.map(Fraction::from).unwrap_or_default();
Ok(Self {
denominator: *amount.denom().unwrap(),
numerator: *amount.numer().unwrap(),
assessment_method: match pb.net_of_transfers {
true => FeeAssessmentMethod::Exclusive,
false => FeeAssessmentMethod::Inclusive,
},
maximum_amount: pb.maximum_amount,
minimum_amount: pb.minimum_amount,
})
}
}
impl ToProtobuf for FractionalFeeData {
type Protobuf = services::FractionalFee;
fn to_protobuf(&self) -> Self::Protobuf {
Self::Protobuf {
fractional_amount: Some(Fraction::new(self.numerator, self.denominator).into()),
minimum_amount: self.minimum_amount,
maximum_amount: self.maximum_amount,
net_of_transfers: matches!(self.assessment_method, FeeAssessmentMethod::Exclusive),
}
}
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct RoyaltyFeeData {
pub denominator: u64,
pub numerator: u64,
pub fallback_fee: Option<FixedFeeData>,
}
impl FromProtobuf<services::RoyaltyFee> for RoyaltyFeeData {
fn from_protobuf(pb: services::RoyaltyFee) -> crate::Result<Self> {
let amount = pb.exchange_value_fraction.unwrap_or_default();
Ok(Self {
denominator: amount.denominator as u64,
numerator: amount.numerator as u64,
fallback_fee: Option::from_protobuf(pb.fallback_fee)?,
})
}
}
impl ToProtobuf for RoyaltyFeeData {
type Protobuf = services::RoyaltyFee;
fn to_protobuf(&self) -> Self::Protobuf {
Self::Protobuf {
fallback_fee: self.fallback_fee.to_protobuf(),
exchange_value_fraction: Some(services::Fraction {
numerator: self.numerator as i64,
denominator: self.denominator as i64,
}),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum FeeAssessmentMethod {
Inclusive,
Exclusive,
}