Protocol Calculations

On-chain calculations used throughout the Coalesce Finance protocol.

Constants

WAD = 10^18                    // Precision constant
BPS = 10,000                   // Basis points denominator
SECONDS_PER_YEAR = 31,536,000  // 365 days
SECONDS_PER_DAY = 86,400       // 24 hours
DAYS_PER_YEAR = 365            // Used for daily rate
USDC_DECIMALS = 6              // USDC decimal places

Interest and Scale Factor

Scale Factor Update

Interest compounds daily. Elapsed time is split into whole days and remaining seconds:

days_elapsed = elapsed_seconds / 86400
remaining_seconds = elapsed_seconds % 86400
daily_rate_wad = annual_interest_bps × WAD / (365 × BPS)

// Step 1: Compound for whole days (exponentiation by squaring)
new_scale_factor = old_scale_factor × pow_wad(WAD + daily_rate_wad, days_elapsed) / WAD

// Step 2: Linear for remaining sub-day seconds
sub_day_delta = annual_interest_bps × remaining_seconds × WAD / (SECONDS_PER_YEAR × BPS)
new_scale_factor = new_scale_factor × (WAD + sub_day_delta) / WAD

pow_wad(base, exp) computes base^exp in WAD precision using binary exponentiation (O(log n) multiplications).

Example:

APR = 8% (800 bps)
Elapsed = 90 days exactly (7,776,000 seconds)
Old Scale = 1.0 × 10^18

daily_rate_wad = 800 × 10^18 / (365 × 10,000)
               = 219,178,082,191,780

new_scale_factor = 10^18 × pow_wad(10^18 + 219,178,082,191,780, 90) / 10^18
                 ≈ 1,019,920,553,041,990,000
                 ≈ 1.01992 × 10^18

This produces deterministic results regardless of transaction frequency.

Deposits and Withdrawals

Deposit: USDC → Shares

shares = deposit_amount × WAD / scale_factor

Example:

Deposit = 10,000 USDC (10,000,000,000 in 6-decimal)
Scale Factor = 1.02 × 10^18

shares = 10,000,000,000 × 10^18 / (1.02 × 10^18)
       = 9,803,921,568,627,450,980,392
       ≈ 9,803.92 shares (in WAD)

Withdrawal: Shares → USDC

usdc_value = shares × scale_factor / WAD

Example (8% APR, 1 year, daily compounding):

Shares = 9,803,921,568,627,450,980,392
Scale Factor = 1.08328 × 10^18

usdc_value = 9,803,921,568,627,450,980,392 × 1.08328 × 10^18 / 10^18
           ≈ 10,620,392,156,862,745,098
           ≈ 10,620.39 USDC

Settlement

Settlement Factor Calculation

fees_reserved = min(vault_balance, accrued_protocol_fees)
available_for_lenders = vault_balance - fees_reserved
raw_factor = available_for_lenders × WAD / total_expected_value
settlement_factor = max(1, min(WAD, raw_factor))

If total_expected_value = 0, the program sets settlement_factor = WAD.

Where:

  • vault_balance = USDC in vault at settlement
  • accrued_protocol_fees = fees accrued but not yet collected
  • available_for_lenders = amount available to lender payouts after fee reservation
  • total_expected_value = total shares × scale_factor / WAD

Example:

Vault Balance = 80,000 USDC
Accrued Protocol Fees = 1,000 USDC
Available For Lenders = 79,000 USDC
Total Shares = 100,000 × 10^18 (WAD)
Scale Factor = 1.08328 × 10^18  (8% APR, 1 year, daily compounding)

total_expected = 100,000 × 10^18 × 1.08328 × 10^18 / 10^18
               = 108,328 × 10^18 USDC (in WAD)
               = 108,328 USDC

raw_factor = 79,000 × 10^18 / 108,328
           ≈ 729,267,000,000,000,000
           ≈ 0.7293 (72.93%)

settlement_factor = max(1, min(WAD, raw_factor))

Payout Calculation

payout = shares × scale_factor × settlement_factor / WAD²

Example:

Shares = 10,000 × 10^18
Scale Factor = 1.08328 × 10^18  (8% APR, 1 year, daily compounding)
Settlement Factor = 0.75 × 10^18

payout = 10,000 × 10^18 × 1.08328 × 10^18 × 0.75 × 10^18 / (10^18)²
       ≈ 8,124.60 USDC

Capacity and Borrowability

Fill Rate (Capacity Check)

The deposit cap is enforced against current normalized supply, not cumulative counters:

current_normalized_supply = scaled_total_supply × scale_factor / WAD
fill_rate = current_normalized_supply / max_total_supply × 100%

Available to Borrow (On-Chain Check)

Borrow availability is based on live vault balance with fee reservation:

fees_reserved = min(vault_balance, accrued_protocol_fees)
available_to_borrow = vault_balance - fees_reserved

Borrow enforces:

borrow_amount <= available_to_borrow

total_deposited, total_borrowed, and total_repaid are cumulative counters for analytics. They are not used for borrowability checks.

Fee Calculations

Protocol Fee Accrual (Per Accrual Step)

Protocol fees are accrued from each interest step using the post-interest scale factor:

interest_delta_wad = new_scale_factor × WAD / scale_factor - WAD
fee_delta_wad = interest_delta_wad × fee_rate_bps / BPS
fee_normalized = scaled_total_supply × new_scale_factor / WAD × fee_delta_wad / WAD

interest_delta_wad is derived from the ratio of the post-accrual scale factor to the pre-accrual scale factor, capturing compound growth from daily compounding (see Interest Accrual).

This makes the effective fee rate slightly higher than the nominal fee_rate_bps / BPS for each accrual step. The fee remains an additional borrower charge and does not reduce lender interest.

Example (8% APR, 10,000 USDC, 1 year, daily compounding, 10% fee, approximate):

Interest Earned ≈ 832.78 USDC
Protocol Fee ≈ 83.28 USDC (slightly higher in exact per-accrual math due to post-interest scaling)
Total borrower obligation ≈ 10,916.05 USDC

Borrower Total Obligation

total_obligation = principal + gross_interest + fee

Note: the protocol fee is charged in addition to lender interest — it does not reduce what lenders receive (see Fees).

Time Calculations

Days to Maturity

days_remaining = (maturity_timestamp - current_timestamp) / 86400

Term Progress

progress = (current_timestamp - start_timestamp) / (maturity_timestamp - start_timestamp)

Annualized Return

annualized = (final / initial)^(365/days) - 1

Or linear approximation:

annualized ≈ (final / initial - 1) × (365 / days)

Precision Notes

WAD Precision (10^18)

  • Used for: APR, scale factor, settlement factor, shares
  • Prevents precision loss in fractional calculations
  • All intermediate calculations maintain WAD precision

USDC Precision (10^6)

  • Used for: token amounts
  • Final outputs converted from WAD to USDC precision

Overflow Prevention

All calculations use checked arithmetic:

let result = a.checked_mul(b)?.checked_div(c)?;

Maximum safe values with u128:

  • WAD × WAD: Safe (10^36 < 2^128)
  • WAD × WAD × WAD: Overflow risk, requires intermediate division