Fees
Two kinds of protocol fees: borrow interest and redemption fees. They have very different destinations.
This page traces both. User views of the staking destinations are in Earning.
What gets charged, and where it goes
| Fee | Charged in | Destination |
|---|---|---|
| Borrow interest | RD | Accrued continuously via InterestEngine.drip() into trove debt. Routed through FeeRouter and GlobalFeeRouter to Stability Pool, LP staking, FEE staking, keeper rewards, and (when active) bad-debt repayment. |
| Redemption fee | Branch collateral | Retained by the redeemed trove as residual collateral. Does not flow to the routers; does not reach FEE stakers. The only exception is the bad-debt sink case (see below). |
There is no upfront borrow fee (Liquity v1 had one; this protocol does not). The borrow rate accrues continuously instead.
Borrow-interest path
This is the path that produces staker income.
Stage 1: branch FeeRouter
Each branch has a FeeRouter. When the branch's InterestEngine accrues interest, the branch calls into the router. The router:
- Sends a fraction to the branch's local
StabilityPool(folded into deposit balances, growing depositor RD without their action). - Sends the rest to the singleton
GlobalFeeRouter.
The SP split adapts to SP utilization:
SP_utilization = stability_pool_balance / branch_debt
target = 25%
deadband = 5%
allocation_bound = [10%, 60%]
bias = 50%
half-life of EMA = 72 hours
If SP utilization is above target (the pool is large relative to debt; depositors are over-supplied), the router routes less to the SP and more to global stakers. If SP utilization is below target (the pool is thin; risk of unhandled liquidation), the router routes more to the SP, growing depositor balances faster and incentivizing fresh deposits.
The PI gains:
kp = 0.5
timeConstant = 3 × half-life = 9 days
ki = kp / timeConstant
Slow but steady. The router does not whipsaw the SP split day-to-day. Initial spAllocFrac output is the 50% bias.
Stage 2: GlobalFeeRouter
The singleton GlobalFeeRouter receives the cross-branch share. It splits across:
- Keeper rewards. First call. The router tops up
keeperRewardstoward a target balance. - Bad-debt repayment. If a branch has been marked
currentUnderCollateralizedTM, half of the staker allocation is diverted to clear that branch's debt before staker distribution. - LP staking. The BPT stakers' share, controlled via PI on LP utilization.
- FEE staking. The remainder, to FEE stakers (across all tiers), as RD.
Keeper rewards
keeperRewardsTargetBalance = 1,000 RD
keeperRewardsMinBalance = 500 RD
If keeperRewards RD balance is below targetBalance, the router routes funds there until restored. The KeeperRewards contract is what pays the per-second drips and oracle update rewards (drip max 425 RD, par update max 10 RD, and so on).
Bad-debt repayment
UNDERCOLLATERALIZED_FRAC = 50%
When a branch has bad debt, 50% of the staker allocation is held back and used to call payDebt(...) on the sink branch. Stakers receive only the remaining 50% during such periods.
This is the autonomous solvency-restoration mechanism. No admin top-up needed. The protocol slowly bleeds fees into bad-debt repair until the sink branch is healthy again.
LP vs FEE split
The same PI-controlled allocation pattern, with different targets:
target_LP_utilization = 8%
deadband = 1%
allocation_bound = [10%, 60%]
bias = 50%
kp = 0.2
ki = kp / (3 × 72h)
LP_utilization = total_BPT_staked × spot_BPT_USD_value / system_RD_debt. The protocol wants roughly 8% of system-debt-equivalent in the AMM as paired liquidity. If it's lower, LP gets more incentive; if higher, LP gets less.
The other half goes to FEE stakers, distributed across the three tiers in proportion to stake × tier_multiplier. See FEE staking.
Redemption-fee path
Redemption fees don't flow through the routers at all. From RedemptionLot.sol:
collateralFeeis "Fee retained by the trove."
Mechanically:
gross_collateral_drawn = (redeemed_rd / oracle_price) × par
fee_in_collateral = gross_collateral_drawn × redemption_fee
collateral_to_redeemer = gross_collateral_drawn − fee_in_collateral
The trove's debt decreases by the full redeemed_rd (paid down at par). The trove's collateral decreases by only collateral_to_redeemer. The fee portion stays inside the trove as residual collateral, raising the trove's post-redemption ICR.
Why this design: it rewards borrowers for staying above the redemption frontier. A borrower whose trove gets partially redeemed against ends up with less debt and an improved ICR, partially compensated for the inconvenience of being chosen.
Exception: bad-debt sink
There is one path where the redemption fee does flow rather than being retained. When the Aggregator has designated a currentUnderCollateralizedTM (a sink branch with bad debt), every redemption on healthy branches:
- Converts the fee that would have been retained by the redeemed trove into RD.
- Routes that RD to the sink branch's
payDebt(...)to chip away at the bad debt.
So in the bad-debt-sink case, the redemption fee funds protocol solvency repair. In the normal case, it stays with the borrower. Either way, it never flows to FEE stakers.
See Redemption mechanism for the full bad-debt flow.
Visual
Branch 1 Branch 2 Branch 3
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ FeeRouter │ │ FeeRouter │ │ FeeRouter │
└──┬────────┬──┘ └──┬────────┬──┘ └──┬────────┬──┘
│ │ │ │ │ │
│ ▼ │ ▼ │ ▼
│ ┌─────┐ │ ┌─────┐ │ ┌─────┐
│ │ SP │ │ │ SP │ │ │ SP │
│ └─────┘ │ └─────┘ │ └─────┘
│ │ │
└──────────────┬───────────┴───────────┬──────────────┘
▼ ▼
┌──────────────────────────────┐
│ GlobalFeeRouter │┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
└──────┬─────────┬──────────┬──┘ ┊
│ │ │ primary ┊ dotted: active only
▼ ▼ ▼ ▼ when a branch is
┌────────┐ ┌────────┐ ┌──────────┐ ┌───────────┐ undercollateralized
│ LP │ │ FEE │ │ Keeper │ │ sink │
│Staking │ │Staking │ │ Rewards │ │ branch │
└────────┘ └────────┘ └──────────┘ │ payDebt() │
└───────────┘
Solid arrows are the normal-operation flow: each branch's FeeRouter splits borrow interest between its local Stability Pool and GlobalFeeRouter, which fans out to LP staking, FEE staking, and keeper rewards.
The dotted arrow is the bad-debt path. It is secondary and temporary, active only when a branch is undercollateralized. While active, UNDERCOLLATERALIZED_FRAC = 50% of the staker allocation, plus diverted redemption fees, are routed to the sink branch's payDebt(...) instead of to LP and FEE stakers. Once the sink branch is healthy again, this path turns off and normal flow resumes.
Redemption fees do not enter this diagram in normal operation; they are retained by the redeemed trove (see "Redemption-fee path" above). The only exception is the bad-debt-sink case, in which the redemption fee feeds the dotted path along with the staker diversion.
Why two stages and two PI loops on the borrow path
- Per-branch SP utilization is local. A branch's SP can be over- or under-supplied independently. Putting the SP allocator on the branch's
FeeRouterlets each branch tune its own SP incentives. - LP utilization is global. One canonical pool with one TWAP, one liquidity number. So the LP allocator lives on the singleton
GlobalFeeRouter. - Decoupling. SP and LP have very different time constants. Branching them makes the controllers independently tunable without one's noise feeding into the other's loop.
Where to dig deeper
- FEE staking, LP staking, Stability Pool for staker views of each destination.
- Redemption mechanism for the redeemed-trove retention and the bad-debt sink path.
- Interest model for the borrow-rate origin.