FeeManager
TheFeeManager contract (Solidity name NotareumFeeManager) centralizes every NOTA-denominated fee the protocol charges, routes collected fees to the treasury, the burn address, and operators, and exposes governance-only setters for the fee parameters. It is the single payment gateway: NotaRegistry, VerificationEngine, and SlashingManager all call through it rather than handling transfers themselves.
Responsibilities
- Quote fees for each protocol operation.
- Collect fees (either via ERC-20
transferFromor via the attached ETH paid on the origin call when applicable). - Split collected fees between treasury, burn, and optional validator rewards pool.
- Maintain the fee tables that
RegistryOps,VerifyOps, andAttestationOpsall query. - Refund partial fees on expired verification requests.
Fee categories
Defaults are governance-set on deployment. All numbers above are the proposed launch values in the spec; the contract reads them from storage set at initialization.
Splits
Every collected fee routes through three buckets:
The validator pool feeds the attestation reward distribution inside
ValidatorStaking. The burn share goes to address(0xdead) and is permanently removed from circulation. Bps are adjustable via governance.
Data structures
Functions
quote
OpKind enum lists every chargeable action: Register, Update, VerifyBasic, VerifyEnhanced, VerifyInstitutional, DisputeBondBasic, DisputeBondEnhanced, DisputeBondInstitutional.
collect
payer (requires ERC-20 allowance on NOTA) and applies the split. Callable only by the pre-wired protocol contracts (NotaRegistry, VerificationEngine, SlashingManager). Emits FeeCollected(op, payer, fee, treasuryShare, burnShare, validatorShare).
refund
VerificationEngine when a verification request expires without reaching quorum. Refunds the unused portion of the fee from the validator pool (or directly from the contract balance). Emits FeeRefunded(op, payee, amount).
Governance setters
TREASURY_ADMIN role is held by a timelocked governance executor; the default delay is 14 days.
distribute
collect accumulates fees without transferring on every call. Emits FeesDistributed(treasury, burned, validatorPool).
Fee accounting
Per-bucket balances accumulate inside the contract and are flushed ondistribute or on threshold crossings inside collect. This amortizes the cost of split transfers across many operations, particularly important for high-frequency attestation flows.
flushThreshold, the next collect auto-flushes all three. distribute forces a flush regardless.
Validation
setSplit reverts if treasuryBps + burnBps + validatorPoolBps != 10000. setFeeTable reverts if any fee exceeds the governance-set absolute cap (e.g., 500_000 NOTA) to protect against malicious or mistaken proposals.
Integration wiring
FeeManager knows three callers by address, set at initialization:
Any other caller reverts on
collect/refund/distribute. The wiring is set once via initialize and is immutable after that (changing it requires an upgrade).
Events
SDK usage
ntm.fee.governance.* to keep the normal read API clean.
Invariants
treasuryBps + burnBps + validatorPoolBps == 10000.- Every
collectincrements exactly one of pending buckets (ordistributeflushes them), never two at once, never creating or destroying NOTA. - Refunds never exceed the original fee collected for the refunded operation.
- Only pre-wired caller contracts can invoke
collect/refund.

