FeeCollector¶
Stateless fee-assessment library for MultiStrategyVault. Pulls quotes from Accountant, applies TVL cap + rate-limit checks. Not deployed — logic inlined in MSV (from . import FeeCollector breaks titanoboa 0.2.8 deploy).
Source: khomdev-keep/src/FeeCollector.vy (Vyper 0.4.x)
Consumer: MSV _maybe_assess_fees (via inlined equivalent) on report
Implementation overview¶
is_rate_limited— at most one fee mint perMIN_FEE_INTERVAL(1 hour)compute_fee— callsAccountant.assess, hard-caps total atMAX_FEE_PER_ASSESS_BPS(20% TVL), returns recipient fromfee_recipient- Defence-in-depth — bounds rogue/misconfigured accountant: ~20%/hour worst case vs ~20%/block
flowchart LR
MSV[MSV report] --> FC[FeeCollector logic]
FC --> ACC[Accountant.assess]
FC -->|mgmt + perf capped| MINT[mint fee shares]
Constants¶
| Name | Value | Role |
|---|---|---|
MAX_FEE_PER_ASSESS_BPS |
2000 (20%) | Per-call TVL cap |
MIN_FEE_INTERVAL |
3600 s | Min time between assessments |
Internal functions¶
is_rate_limited¶
FeeCollector.is_rate_limited(last_fee_assess) -> bool
Whether a fee assessment would be blocked by the hourly rate limit.
| Param | Type | Description |
|---|---|---|
last_fee_assess |
uint256 |
Vault's last_fee_assess timestamp |
Returns: bool — True if block.timestamp < last_fee_assess + MIN_FEE_INTERVAL. First assessment (last_fee_assess == 0) always allowed.
Access: @internal @view — inlined in MSV _maybe_assess_fees.
Source (khomdev-keep/src/FeeCollector.vy:34-40):
@internal
@view
def is_rate_limited(last_fee_assess: uint256) -> bool:
if last_fee_assess == 0:
return False
return block.timestamp < last_fee_assess + MIN_FEE_INTERVAL
Example:
# MSV checks before calling Accountant
if not FeeCollector.is_rate_limited(self.last_fee_assess):
...
Cross-links: compute_fee · Accountant assess
compute_fee¶
FeeCollector.compute_fee(accountant, total_assets, total_supply) -> (uint256, uint256, address)
Pull fee quote from Accountant, apply 20% TVL cap, return mint recipient.
| Param | Type | Description |
|---|---|---|
accountant |
address |
Accountant contract; 0x0 → no-op |
total_assets |
uint256 |
Vault TVL at assessment |
total_supply |
uint256 |
Vault share supply |
Returns: (mgmt_fee, perf_fee, recipient) — underlying asset units. All zero + empty recipient when no fee due. Over-cap fees prorated between mgmt/perf.
Access: @internal — inlined in MSV fee path.
Source (khomdev-keep/src/FeeCollector.vy:44-83):
@internal
def compute_fee(accountant, total_assets, total_supply) -> (uint256, uint256, address):
if accountant == empty(address) or total_assets == 0 or total_supply == 0:
return (0, 0, empty(address))
mgmt_fee, perf_fee = extcall IAccountant(accountant).assess(total_assets, total_supply)
cap = total_assets * MAX_FEE_PER_ASSESS_BPS // MAX_BPS
# prorate if over cap; fetch fee_recipient
return (mgmt_fee, perf_fee, recipient)
Example:
Cross-links: is_rate_limited · Accountant assess · MultiStrategyVault report