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 settlementaccrued_protocol_fees= fees accrued but not yet collectedavailable_for_lenders= amount available to lender payouts after fee reservationtotal_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