use elements::Transaction;
use crate::{
transaction::{
TxError,
liquid::{CLAIM_VSIZE, OPENING_VSIZE, REFUND_VSIZE},
},
types::SwapType,
};
pub(crate) fn liquid_fee_for_tx(tx: &Transaction, fee_rate: f64) -> Result<u64, TxError> {
if !fee_rate.is_finite() || fee_rate < 0.0 {
return Err(TxError::InvalidFeeRate(fee_rate));
}
fee_for_vsize(tx.discount_vsize() as u64, fee_rate).ok_or(TxError::FeeOverflow)
}
pub fn fee_for_vsize(vsize: u64, fee_rate: f64) -> Option<u64> {
if !fee_rate.is_finite() || fee_rate < 0.0 {
return None;
}
let fee = (vsize as f64 * fee_rate).ceil();
(fee <= u64::MAX as f64).then_some(fee as u64)
}
pub fn base_fee(swap_type: SwapType, fee_rate: f64, safety: f64) -> u64 {
let vsize = match swap_type {
SwapType::SwapIn => CLAIM_VSIZE,
SwapType::SwapOut => OPENING_VSIZE + REFUND_VSIZE,
SwapType::SwapChain => OPENING_VSIZE + REFUND_VSIZE + CLAIM_VSIZE,
};
let fee = fee_for_vsize(vsize, fee_rate).unwrap_or(u64::MAX);
((fee as f64) * safety).ceil() as u64
}
#[cfg(test)]
mod tests {
use elements::{LockTime, Transaction};
use super::*;
#[test]
fn base_fee_by_swap_type() {
assert_eq!(base_fee(SwapType::SwapIn, 10.0, 1.0), 10 * CLAIM_VSIZE);
assert_eq!(
base_fee(SwapType::SwapOut, 10.0, 1.0),
10 * (OPENING_VSIZE + REFUND_VSIZE)
);
assert_eq!(
base_fee(SwapType::SwapChain, 10.0, 1.0),
10 * (OPENING_VSIZE + REFUND_VSIZE + CLAIM_VSIZE)
);
assert_eq!(
base_fee(SwapType::SwapIn, 10.0, 1.5),
base_fee(SwapType::SwapIn, 10.0, 1.0) * 3 / 2,
);
}
#[test]
fn base_fee_at_the_relay_minimum() {
assert_eq!(base_fee(SwapType::SwapIn, 0.1, 1.0), 25);
assert_eq!(base_fee(SwapType::SwapOut, 0.1, 1.0), 60);
}
#[test]
fn base_fee_saturates_instead_of_overflowing() {
assert_eq!(base_fee(SwapType::SwapIn, f64::MAX, 1.0), u64::MAX);
}
#[test]
fn transaction_fee_reports_an_overflowing_rate() {
let tx = Transaction {
version: 2,
lock_time: LockTime::ZERO,
input: vec![],
output: vec![],
};
assert!(matches!(
liquid_fee_for_tx(&tx, f64::MAX),
Err(TxError::FeeOverflow)
));
}
#[test]
fn transaction_fee_rejects_invalid_rate() {
let tx = Transaction {
version: 2,
lock_time: LockTime::ZERO,
input: vec![],
output: vec![],
};
assert!(matches!(
liquid_fee_for_tx(&tx, -1.0),
Err(TxError::InvalidFeeRate(rate)) if rate == -1.0
));
}
}