Skip to main content

FeeProcessor

contracts/src/index/FeeProcessor.sol — the permissionless engine that turns accrued in-token fees into the protocol/RFV/holder split. It replaces the trusted keeper of earlier designs: anyone may call it, and it holds no funds between calls.

convert

function convert(
address index,
uint256 minReserveOut, // min USDG from selling the fee tokens (> 0)
uint256[] calldata stockMinOuts, // per-stock min out (> 0 each, length == basket)
uint256 deadline
) external nonReentrant;

Execution:

  1. Pull. index.releaseFees() transfers the accrued fee tokens to the processor (untaxed — the processor is fee-exempt). Reverts NothingToProcess if zero.
  2. Swap. Fee tokens → reserve asset (USDG) via the registered v2-style router, bounded by minReserveOut.
  3. Split. protocolCut = 10% (fixed) → protocol treasury; rfvCut = rfvShareBps → the index's RFV treasury; distCut = remainder.
  4. Buy & distribute. distCut is spent evenly across the basket buying each stock, each transferred to the IndexToken followed by distribute(stock) — dividends are live in the same transaction.

Emits Converted(index, feeTokens, reserveIn, protocolCut, rfvCut, distSpent).

Robustness (audit-hardened)

  • One bad stock can't brick the basket (finding V2): each stock buy runs under try/catch; a delisted / liquidity-less / reverting leg is skipped and its budget stays in USDG, while the others distribute normally.
  • The skip path can't be gamed: the effective min-out per stock is capped at the router's own getAmountsOut quote, so a griefer can't pass absurd min-outs to force skips and reroute value.
  • No silent no-ops: if nothing was distributed and nothing went to RFV (distSpent == 0 on a distributing index), the call reverts instead of consuming fees for nothing; zero-quote legs are skipped.
  • Pure-RFV indexes (rfvShareBps = 9000) skip the stock loop entirely — conversion is just swap + split.

Slippage: caller-supplied, oracle-floored on mainnet

All min-outs must be > 0 — zero-bound conversions revert. The bounds are caller-supplied, which is safe on the testnet's 1:1 mock router; on mainnet callers should derive floors from the per-asset Chainlink feeds that exist for every Robinhood Chain tokenized equity (or a TWAP) to close the sandwich vector on thin pools. The optional keeper/ script does exactly this computation.

Mainnet routing

Robinhood Chain tokenized stocks trade via RFQ/0x at launch, not necessarily v2 AMM pools. The v2-router path is the testnet-mock wiring; mainnet deployment will route stock buys accordingly (see Security → mainnet gates).