Skip to main content

FeeManager

The FeeManager 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 transferFrom or 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, and AttestationOps all 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:
Default split: 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

Returns the NOTA fee for the operation. The OpKind enum lists every chargeable action: Register, Update, VerifyBasic, VerifyEnhanced, VerifyInstitutional, DisputeBondBasic, DisputeBondEnhanced, DisputeBondInstitutional.

collect

Pulls the fee from 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

Used by 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

All setters are delay-gated through AccessManager. The TREASURY_ADMIN role is held by a timelocked governance executor; the default delay is 14 days.

distribute

Callable by anyone. Flushes any pending per-bucket balances to their destinations. Used as a gas-optimization path when 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 on distribute or on threshold crossings inside collect. This amortizes the cost of split transfers across many operations, particularly important for high-frequency attestation flows.
When any pending balance crosses 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

Governance-only writes are exposed on the SDK as ntm.fee.governance.* to keep the normal read API clean.

Invariants

  1. treasuryBps + burnBps + validatorPoolBps == 10000.
  2. Every collect increments exactly one of pending buckets (or distribute flushes them), never two at once, never creating or destroying NOTA.
  3. Refunds never exceed the original fee collected for the refunded operation.
  4. Only pre-wired caller contracts can invoke collect / refund.