Skip to content

Latest commit

 

History

History
330 lines (252 loc) · 9.24 KB

File metadata and controls

330 lines (252 loc) · 9.24 KB

Fee Calculation Service Refactor

Overview

Successfully centralized all fee calculation logic into a dedicated fee_service module that provides:

  • Unified fee calculation interface - Single source of truth for all fee logic
  • Corridor-based fee configurations - Country-to-country specific fee rules
  • Detailed fee breakdowns - Complete transparency of all fee components
  • No logic duplication - All fee calculations route through centralized service

Architecture

Core Components

1. Fee Service Module (src/fee_service.rs)

The central fee calculation engine with the following key functions:

// Primary entry point - calculates complete fee breakdown
pub fn calculate_fees_with_breakdown(
    env: &Env,
    amount: i128,
    corridor: Option<&FeeCorridor>,
) -> Result<FeeBreakdown, ContractError>

// Backward compatible - calculates only platform fee
pub fn calculate_platform_fee(
    env: &Env,
    amount: i128,
) -> Result<i128, ContractError>

// Batch processing - aggregates fees for multiple transactions
pub fn calculate_batch_fees(
    env: &Env,
    amounts: &[i128],
    corridor: Option<&FeeCorridor>,
) -> Result<FeeBreakdown, ContractError>

2. Data Structures

FeeCorridor - Country-to-country fee configuration:

pub struct FeeCorridor {
    pub from_country: String,      // ISO 3166-1 alpha-2 code
    pub to_country: String,         // ISO 3166-1 alpha-2 code
    pub strategy: FeeStrategy,      // Fee calculation strategy
    pub protocol_fee_bps: Option<u32>, // Optional protocol fee override
}

FeeBreakdown - Complete fee transparency:

pub struct FeeBreakdown {
    pub amount: i128,               // Original transaction amount
    pub platform_fee: i128,         // Platform fee charged
    pub protocol_fee: i128,         // Protocol fee charged
    pub total_fees: i128,           // Sum of all fees
    pub net_amount: i128,           // Amount after fees
    pub strategy_used: FeeStrategy, // Strategy applied
    pub corridor_applied: Option<FeeCorridor>, // Corridor if used
}

Fee Strategies

The service supports three fee calculation strategies:

  1. Percentage - Fee based on percentage of amount (basis points)

    • Example: FeeStrategy::Percentage(250) = 2.5%
  2. Flat - Fixed fee regardless of amount

    • Example: FeeStrategy::Flat(100) = 100 units
  3. Dynamic - Tiered fees based on amount ranges

    • Example: FeeStrategy::Dynamic(400) = 4% base
    • <1000: 4%, 1000-10000: 2%, >10000: 1%

Integration Points

1. Contract Initialization

No changes required - existing initialization works with new service.

2. Remittance Creation

Before:

let strategy = get_fee_strategy(&env);
let fee = calculate_fee(&env, &strategy, amount)?;

After:

let fee = fee_service::calculate_platform_fee(&env, amount)?;

3. Payout Confirmation

Before:

let protocol_fee_bps = get_protocol_fee_bps(&env);
let protocol_fee = remittance.amount
    .checked_mul(protocol_fee_bps as i128)?
    .checked_div(10000)?;
let payout_amount = remittance.amount
    .checked_sub(remittance.fee)?
    .checked_sub(protocol_fee)?;

After:

let fee_breakdown = fee_service::calculate_fees_with_breakdown(
    &env,
    remittance.amount,
    None,
)?;
let payout_amount = fee_breakdown.net_amount;
let protocol_fee = fee_breakdown.protocol_fee;

New Public API Methods

Fee Breakdown Calculation

// Calculate fees without corridor
pub fn calculate_fee_breakdown(
    env: Env,
    amount: i128,
) -> Result<FeeBreakdown, ContractError>

// Calculate fees with corridor-specific rules
pub fn calculate_fee_breakdown_with_corridor(
    env: Env,
    amount: i128,
    corridor: FeeCorridor,
) -> Result<FeeBreakdown, ContractError>

Corridor Management

// Set corridor configuration (admin only)
pub fn set_fee_corridor(
    env: Env,
    caller: Address,
    corridor: FeeCorridor,
) -> Result<(), ContractError>

// Get corridor configuration
pub fn get_fee_corridor(
    env: Env,
    from_country: String,
    to_country: String,
) -> Option<FeeCorridor>

// Remove corridor configuration (admin only)
pub fn remove_fee_corridor(
    env: Env,
    caller: Address,
    from_country: String,
    to_country: String,
) -> Result<(), ContractError>

Usage Examples

Example 1: Calculate Fee Breakdown

// Get complete fee breakdown for a transaction
let breakdown = client.calculate_fee_breakdown(&10000);

println!("Amount: {}", breakdown.amount);           // 10000
println!("Platform Fee: {}", breakdown.platform_fee); // 250 (2.5%)
println!("Protocol Fee: {}", breakdown.protocol_fee); // 100 (1%)
println!("Total Fees: {}", breakdown.total_fees);     // 350
println!("Net Amount: {}", breakdown.net_amount);     // 9650

Example 2: Configure Corridor-Based Fees

// Set up US -> Mexico corridor with lower fees
let corridor = FeeCorridor {
    from_country: String::from_str(&env, "US"),
    to_country: String::from_str(&env, "MX"),
    strategy: FeeStrategy::Percentage(150), // 1.5% instead of default 2.5%
    protocol_fee_bps: Some(50),             // 0.5% protocol fee
};

client.set_fee_corridor(&admin, &corridor);

// Calculate fees using corridor
let breakdown = client.calculate_fee_breakdown_with_corridor(
    &10000,
    &corridor,
);

println!("Platform Fee: {}", breakdown.platform_fee); // 150 (1.5%)
println!("Protocol Fee: {}", breakdown.protocol_fee); // 50 (0.5%)
println!("Net Amount: {}", breakdown.net_amount);     // 9800

Example 3: Retrieve Corridor Configuration

// Get existing corridor configuration
let corridor = client.get_fee_corridor(
    &String::from_str(&env, "US"),
    &String::from_str(&env, "MX"),
);

if let Some(corr) = corridor {
    println!("Corridor exists: {} -> {}", corr.from_country, corr.to_country);
}

Benefits

1. Centralization

  • Single source of truth - All fee logic in one module
  • Easier maintenance - Changes in one place affect entire system
  • Reduced bugs - No duplicate logic to keep in sync

2. Transparency

  • Complete breakdowns - Users see all fee components
  • Audit trail - Fee calculations are traceable
  • Validation - Built-in consistency checks

3. Flexibility

  • Corridor support - Country-specific fee rules
  • Multiple strategies - Percentage, flat, or dynamic fees
  • Protocol fees - Separate treasury fees

4. Correctness

  • Overflow protection - All arithmetic uses checked operations
  • Validation - Fee breakdowns self-validate for consistency
  • Type safety - Rust's type system prevents errors

Testing

The fee service includes comprehensive unit tests:

#[test]
fn test_fee_breakdown_validation()
fn test_calculate_fees_percentage()
fn test_calculate_fees_with_corridor()
fn test_batch_fees()
fn test_flat_fee_strategy()
fn test_dynamic_fee_strategy()
fn test_zero_amount_rejected()
fn test_negative_amount_rejected()

All existing tests continue to pass, ensuring backward compatibility.

Migration Path

For Existing Deployments

  1. No breaking changes - Existing contracts continue to work
  2. Gradual adoption - New features can be adopted incrementally
  3. Backward compatible - Old API methods still function

For New Deployments

  1. Use calculate_fee_breakdown() for transparency
  2. Configure corridors for cross-border optimization
  3. Leverage detailed breakdowns in UI/UX

Code Quality

Eliminated Duplication

Before: Fee calculation logic appeared in:

  • src/fee_strategy.rs - Strategy-based calculation
  • src/lib.rs - Protocol fee calculation in confirm_payout
  • Inline calculations scattered across codebase

After: All fee logic centralized in:

  • src/fee_service.rs - Single module with all calculations

Improved Maintainability

  • Clear separation - Fee logic isolated from business logic
  • Documented - Comprehensive inline documentation
  • Tested - Full test coverage of all scenarios
  • Type-safe - Strong typing prevents errors

Performance

  • No overhead - Centralization doesn't add computational cost
  • Efficient - Batch operations optimize multiple calculations
  • Cached - Storage reads minimized through smart design

Security

  • Overflow protection - All arithmetic uses checked operations
  • Validation - Input validation at service boundary
  • Consistency checks - Fee breakdowns self-validate
  • Admin controls - Corridor management requires admin auth

Future Enhancements

Potential future additions to the fee service:

  1. Time-based fees - Different rates for different times
  2. Volume discounts - Lower fees for high-volume users
  3. Currency-specific fees - Different rates per currency
  4. Fee caps - Maximum fee limits
  5. Fee floors - Minimum fee amounts

Conclusion

The fee calculation service refactor successfully:

✅ Centralizes all fee logic into a dedicated module
✅ Supports corridor-based fee configurations
✅ Returns full fee breakdowns for transparency
✅ Eliminates all duplicated fee logic
✅ Maintains backward compatibility
✅ Improves code maintainability
✅ Enhances security and correctness

The refactor meets all acceptance criteria and provides a solid foundation for future fee-related enhancements.