Skip to content

Accountant

Pluggable fee module for MultiStrategyVault. Time-prorated management fee on AUM + high-water-mark performance fee on per-share appreciation. Multi-vault safe — HWM state keyed by calling vault (msg.sender).

Source: khomdev-keep/src/Accountant.vy (Vyper 0.4.x)

Used by: MSV via set_accountantreport_maybe_assess_fees

Implementation overview

  • Mgmt feetotal_assets * mgmt_fee_bps * dt / year / MAX_BPS
  • Perf fee — on strict PPS increase vs per-vault HWM: (pps - hwm) * supply * perf_fee_bps / 1e18 / MAX_BPS
  • First call — per vault: fees = 0; establishes HWM + last_assess baseline only
  • Caps — mgmt ≤ 200 bps (2%/yr); perf ≤ 2000 bps (20% of gain)
  • Trustassess() permissionless; state isolated by msg.sender vault
flowchart LR
    MSV[MultiStrategyVault] -->|assess| ACC[Accountant]
    ACC -->|mgmt + perf assets| MSV
    MSV -->|mint fee shares| FR[fee_recipient]

Constants

Name Value
MAX_MGMT_FEE_BPS 200 (2%/year)
MAX_PERF_FEE_BPS 2000 (20% of gain)
PPS_SCALE 1e18

Storage

Name Role
mgmt_fee_bps / perf_fee_bps Global fee rates
fee_recipient Receives fee shares (vault mints)
hwm_pps_18[vault] Per-vault high-water PPS
last_assess[vault] Per-vault last assessment timestamp

Access control

snekmate access_control exports (DEFAULT_ADMIN_ROLE, grantRole, etc.). Admin sets rates and recipient.

Events

Event When
FeeRatesUpdated Rates changed
FeeRecipientUpdated Recipient rotated
Assessed Vault called assess

Admin

set_fee_rates

Accountant.set_fee_rates(mgmt_bps_, perf_bps_)

Update global management and performance fee rates.

Param Type Description
mgmt_bps_ uint256 Annual mgmt fee bps (≤ MAX_MGMT_FEE_BPS)
perf_bps_ uint256 Performance fee bps on gain (≤ MAX_PERF_FEE_BPS)

Returns / state: Sets mgmt_fee_bps, perf_fee_bps.

Access: DEFAULT_ADMIN_ROLE only.

Events: FeeRatesUpdated(mgmt_fee_bps, perf_fee_bps).

Source (khomdev-keep/src/Accountant.vy:110-119):

@external
def set_fee_rates(mgmt_bps_: uint256, perf_bps_: uint256):
    access_control._check_role(access_control.DEFAULT_ADMIN_ROLE, msg.sender)
    assert mgmt_bps_ <= MAX_MGMT_FEE_BPS, "accountant: mgmt cap"
    assert perf_bps_ <= MAX_PERF_FEE_BPS, "accountant: perf cap"
    self.mgmt_fee_bps = mgmt_bps_
    self.perf_fee_bps = perf_bps_
    log FeeRatesUpdated(mgmt_fee_bps=mgmt_bps_, perf_fee_bps=perf_bps_)

Example:

accountant.set_fee_rates(50, 1000)  # 0.5% mgmt, 10% perf

Cross-links: set_fee_recipient · assess

set_fee_recipient

Accountant.set_fee_recipient(recipient_)

Set address that receives fee shares (vault mints on assessment).

Param Type Description
recipient_ address Non-zero fee recipient

Returns / state: Sets fee_recipient.

Access: DEFAULT_ADMIN_ROLE only.

Events: FeeRecipientUpdated(recipient).

Source (khomdev-keep/src/Accountant.vy:122-129):

@external
def set_fee_recipient(recipient_: address):
    access_control._check_role(access_control.DEFAULT_ADMIN_ROLE, msg.sender)
    assert recipient_ != empty(address), "accountant: zero recipient"
    self.fee_recipient = recipient_
    log FeeRecipientUpdated(recipient=recipient_)

Example:

accountant.set_fee_recipient(treasury)

Cross-links: set_fee_rates · assess · MultiStrategyVault report

Vault hook

assess

Accountant.assess(total_assets, total_supply) -> (uint256, uint256)

Compute fees since last call; update per-vault HWM and last_assess. Called by vault (typically MSV _maybe_assess_fees).

Param Type Description
total_assets uint256 Vault TVL at assessment
total_supply uint256 Vault share supply

Returns: (mgmt_fee_assets, perf_fee_assets) — underlying units. Vault mints equivalent shares to fee_recipient.

Returns / state: Updates last_assess[msg.sender], hwm_pps_18[msg.sender] when PPS rises. First call per vault: (0, 0) + HWM baseline only.

Access: Permissionless. State keyed by msg.sender (calling vault) — cannot corrupt other vaults.

Events: Assessed(vault, mgmt_fee_assets, perf_fee_assets, new_hwm_pps_18).

Source (khomdev-keep/src/Accountant.vy:136-189):

@external
def assess(total_assets, total_supply) -> (uint256, uint256):
    if total_supply == 0 or total_assets == 0 or fee_recipient == empty(address):
        if last_assess[msg.sender] == 0:
            last_assess[msg.sender] = block.timestamp
        return 0, 0
    # mgmt: total_assets * mgmt_fee_bps * dt / year / MAX_BPS
    pps = total_assets * PPS_SCALE // total_supply
    # perf on pps > hwm; first call sets hwm, no fee
    return mgmt_fee, perf_fee

Note: Perf fee uses marked PPS (strategy marks). MSV also rate-limits (1/hr) and caps (20% TVL) via inlined FeeCollector logic. Prefer report() after harvest() when CoilMaker is in the stack.

Example:

mgmt, perf = accountant.assess(tvl, supply)  # normally called by vault

Cross-links: set_fee_rates · set_fee_recipient · MultiStrategyVault report · FeeCollector compute_fee