> ## Documentation Index
> Fetch the complete documentation index at: https://docs.railnet.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Fee management

> How fees are configured, collected, and distributed in Railnet

<Info>This page covers the smart contract implementation details. See [Glossary](/developers/glossary).</Info>

The FeeManager is an optional contract that you can attach to a Multi-Vehicle or Conduit at deployment time. It automates fee calculation and collection across the vehicle lifecycle. Once set during initialization, the FeeManager cannot be changed or removed, ensuring predictability for users.

You can share a single FeeManager across multiple Multi-Vehicle contracts to maintain consistent fee behavior and centralize configuration changes.

## The 4 fee types

Railnet supports four distinct fee types, each calculated and applied at different points in the vehicle lifecycle.

### Performance fee (on earnings)

A fee charged on gains above the high water mark. You only collect when the strategy makes money.

<CodeGroup>
  ```solidity Solidity theme={null}
  High Water Mark: $1,000,000
  Current Assets:  $1,100,000
  Earnings:        $100,000

  // If performance fee = 20%:
  // Fee = $100,000 * 20% = $20,000 (minted as shares)
  // New High Water Mark: $1,100,000

  // Formula:
  // earnings = max(0, currentTotalAssets - lastTotalAssets)
  // performanceFeeAssets = earnings * performanceFeeBps / 10000
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

**Best for:** Hedge fund-style fee models, performance-aligned compensation. Users only pay when the strategy profits.

<Note>
  If the strategy loses value, no performance fees are collected until the share price recovers past the previous high water mark.
</Note>

### Management fee (time-based)

An annualized fee prorated by time elapsed since the last operation. Charged regardless of performance.

<CodeGroup>
  ```solidity Solidity theme={null}
  Total Assets:     1,000,000 USDC
  Management Fee:   2% annual (200 bps)
  Time Elapsed:     30 days (2,592,000 seconds)

  // Fee = totalAssets * managementFeeBps * timeDelta / (10000 * 31536000)
  // Fee = 1,000,000 * 200 * 2,592,000 / (10000 * 31536000)
  // Fee = ~1,644 USDC worth of shares minted
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

**Best for:** Covering ongoing operational costs (gas, infrastructure, monitoring). Provides a predictable revenue stream.

<Warning>
  Management fees accrue continuously based on `block.timestamp`. The first operation after a long idle period may mint substantial shares.
</Warning>

### Deposit fee (entry fee)

A fee charged when users deposit assets. Deducted from shares received during the unlock phase.

<CodeGroup>
  ```solidity Solidity theme={null}
  User Deposits:     10,000 USDC
  Share Price:       $1.00/share
  Expected Shares:   10,000 shares

  // If deposit fee = 1%:
  // Fee = 10,000 * 1% = 100 shares
  // User Receives:   9,900 shares
  // FeeManager Gets: 100 shares
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

**Best for:** Discouraging short-term "hot money" deposits, one-time onboarding costs, aligning incentives for long-term holders.

### Redeem fee (exit fee)

A fee charged when users redeem shares. Deducted from assets received during the unlock phase.

<CodeGroup>
  ```solidity Solidity theme={null}
  User Redeems:      10,000 shares
  Share Price:       $1.00/share
  Expected Assets:   10,000 USDC

  // If redeem fee = 0.5%:
  // Fee = 10,000 * 0.5% = 50 USDC
  // User Receives:   9,950 USDC
  // FeeManager Gets: 50 USDC
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

**Best for:** Discouraging short-term withdrawals, compensating for liquidity management costs, protecting remaining holders from withdrawal impact.

## When fees are applied

| Fee type        | Calculation point                           | Applied as                  |
| --------------- | ------------------------------------------- | --------------------------- |
| **Performance** | `onOperations` (before each deposit/redeem) | Shares minted to FeeManager |
| **Management**  | `onOperations` (before each deposit/redeem) | Shares minted to FeeManager |
| **Deposit**     | `applyFees` (during unlock phase)           | Reduced shares paid to user |
| **Redeem**      | `applyFees` (during unlock phase)           | Reduced assets paid to user |

## Fee recipients and distribution

Fees can be distributed to multiple recipients with configured percentage splits:

<CodeGroup>
  ```solidity Solidity theme={null}
  FeeRecipient[] memory recipients = new FeeRecipient[](3);
  recipients[0] = FeeRecipient({
      target: operatorAddress,
      shareBps: 6000  // 60% to operator
  });
  recipients[1] = FeeRecipient({
      target: daoTreasuryAddress,
      shareBps: 3000  // 30% to DAO treasury
  });
  recipients[2] = FeeRecipient({
      target: developerAddress,
      shareBps: 1000  // 10% to developers
  });
  // Must sum to exactly 10,000 (100%)

  feeManager.setFeeRecipients(recipients);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

## Example configurations

<Tabs>
  <Tab title="Hedge fund model">
    <CodeGroup>
      ```solidity Solidity theme={null}
      Fees({
          performanceFeeBps: 2000,  // 20% of profits
          managementFeeBps: 0,
          depositFeeBps: 0,
          redeemFeeBps: 0
      })
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>

    Aligned with user returns. You only charge on gains.
  </Tab>

  <Tab title="Index fund model">
    <CodeGroup>
      ```solidity Solidity theme={null}
      Fees({
          performanceFeeBps: 0,
          managementFeeBps: 50,  // 0.5% annual
          depositFeeBps: 0,
          redeemFeeBps: 0
      })
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>

    Low-cost passive strategy with minimal management overhead.
  </Tab>

  <Tab title="Active management">
    <CodeGroup>
      ```solidity Solidity theme={null}
      Fees({
          performanceFeeBps: 1000,  // 10% of profits
          managementFeeBps: 200,    // 2% annual
          depositFeeBps: 50,        // 0.5% entry
          redeemFeeBps: 50          // 0.5% exit
      })
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>

    Professional active management with entry/exit barriers.
  </Tab>

  <Tab title="Long-term capital">
    <CodeGroup>
      ```solidity Solidity theme={null}
      Fees({
          performanceFeeBps: 0,
          managementFeeBps: 0,
          depositFeeBps: 100,  // 1% entry fee
          redeemFeeBps: 0
      })
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>

    Encourages committed capital with free exit.
  </Tab>
</Tabs>

## Configuration

### Prerequisites

You need the following roles on your External Access Control, scoped to the **Fee Manager** contract:

| Role                                | Purpose                                           |
| ----------------------------------- | ------------------------------------------------- |
| `FEE_MANAGER_SET_FEES`              | Update fee percentages                            |
| `FEE_MANAGER_SET_FEE_RECIPIENTS`    | Update the list of fee recipients                 |
| `FEE_MANAGER_DISPATCH_ERC20`        | Distribute collected fees to recipients           |
| `FEE_MANAGER_REDEEM_VEHICLE_SHARES` | Trigger redemption of vehicle shares held as fees |

### Set fee percentages

Configure the four fee rates. Values must not exceed the maximum limits set during Fee Manager deployment.

<CodeGroup>
  ```solidity Solidity theme={null}
  FeeManager.Fees memory newFees = FeeManager.Fees({
      performanceFeeBps: 1000,  // 10% performance fee
      managementFeeBps: 200,    // 2% annual management fee
      depositFeeBps: 0,         // No deposit fee
      redeemFeeBps: 50          // 0.5% redeem fee
  });

  feeManager.setFees(newFees);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Warning>
  Fee values must not exceed the `maxFees` ceiling defined at deployment. Attempting to set fees above the maximum will cause the transaction to revert. You can query the current maximums with `feeManager.maxFees()`.
</Warning>

### Fee calculation formulas

<AccordionGroup>
  <Accordion title="Management fee formula">
    The management fee is annualized and prorated by time elapsed:

    ```
    managementFeeAssets = currentTotalAssets * managementFeeBps * timeDelta / (10000 * 31536000)
    ```

    Where `timeDelta` is the seconds elapsed since the last update and `31536000` is the number of seconds in a year (365 days).
  </Accordion>

  <Accordion title="Performance fee formula">
    The performance fee is applied to earnings above the high water mark:

    ```
    earnings = max(0, currentTotalAssets - lastTotalAssets)
    performanceFeeAssets = earnings * performanceFeeBps / 10000
    ```

    The high water mark (`lastTotalAssets`) is updated after each fee collection, so you only pay performance fees on new gains.
  </Accordion>

  <Accordion title="Deposit and redeem fee formulas">
    Transactional fees are deducted from the gross amount:

    ```
    feeAmount = amount * feeBps / 10000
    netAmount = amount - feeAmount
    ```

    For reverse calculations (determining gross amount from a desired net amount):

    ```
    grossAmount = netAmount * 10000 / (10000 - feeBps)
    ```
  </Accordion>
</AccordionGroup>

### Configure fee recipients

Fee recipients define how collected fees are split among addresses. The sum of all recipient shares must equal 10,000 bps (100%).

<CodeGroup>
  ```solidity Solidity theme={null}
  FeeManager.FeeRecipient[] memory recipients = new FeeManager.FeeRecipient[](2);

  // 70% to the operator
  recipients[0] = FeeManager.FeeRecipient({
      target: operatorAddress,
      shareBps: 7000
  });

  // 30% to the DAO treasury
  recipients[1] = FeeManager.FeeRecipient({
      target: daoTreasuryAddress,
      shareBps: 3000
  });

  feeManager.setFeeRecipients(recipients);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Warning>
  Updating recipients causes old recipients to lose access to any uncollected fees. Always call `dispatchERC20` to distribute pending fees **before** changing recipients.
</Warning>

### Collect accumulated fees

Performance and management fees accumulate as vehicle shares held by the Fee Manager. To convert these shares into underlying assets, trigger a redemption.

<Steps>
  <Step title="Monitor accumulated fees">
    Check the FeeManager's share balance in the vehicle to see how much has been collected.

    <CodeGroup>
      ```solidity Solidity theme={null}
      uint256 accumulatedShares = multiVehicle.balanceOf(address(feeManager));
      uint256 sharePrice = multiVehicle.totalAssets() / multiVehicle.totalSupply();
      uint256 feeValue = accumulatedShares * sharePrice;
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>

  <Step title="Redeem vehicle shares">
    The Fee Manager holds vehicle shares from accumulated performance and management fees. Redeem them to convert shares into the underlying asset.

    **Requires:** `FEE_MANAGER_REDEEM_VEHICLE_SHARES` role.

    <CodeGroup>
      ```solidity Solidity theme={null}
      bytes32 querySalt = keccak256(abi.encodePacked(block.timestamp, "fees"));

      (Query memory query, State state) = feeManager.redeemVehicleShares(
          multiVehicle,
          querySalt
      );
      // Progress query until settled
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>

  <Step title="Distribute to recipients">
    Once redeemed, dispatch base assets to configured recipients.

    **Requires:** `FEE_MANAGER_DISPATCH_ERC20` role.

    <CodeGroup>
      ```solidity Solidity theme={null}
      feeManager.dispatchERC20(IERC20(baseAsset));
      // Distributes entire balance according to recipient shares
      ```

      ```typescript TypeScript theme={null}
      // Coming soon
      ```
    </CodeGroup>
  </Step>
</Steps>

### View current configuration

Query the Fee Manager's current state at any time.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Get current fee configuration
  (bytes32 configId, FeeManager.Fees memory currentFees) = feeManager.fees();

  // Get maximum fee limits
  FeeManager.Fees memory maxFees = feeManager.maxFees();

  // Get current recipients
  FeeManager.FeeRecipient[] memory recipients = feeManager.feeRecipients();

  // Get fee cache for a specific vehicle (high water mark, last update, etc.)
  FeeManager.Cache memory vehicleCache = feeManager.cache(address(vehicle));
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

### Preview fee impact

Use the view functions to simulate fee calculations without executing transactions.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Preview performance + management fees that would be collected
  uint256 feeSharesToMint = feeManager.previewOnOperations(
      totalSupply,
      totalAssets,
      assetDecimals,
      sharesDecimals
  );

  // Preview deposit or redeem fee deduction
  (Asset[] memory netAssets, Asset[] memory feeAssets) = feeManager.previewApplyFees(
      configId,
      assets,
      Mode.DEPOSIT
  );

  // Reverse calculation: find gross amount needed for a target net amount
  (Asset[] memory grossAssets, Asset[] memory reverseFeeAssets) = feeManager.reverseFees(
      configId,
      targetNetAssets,
      Mode.DEPOSIT
  );
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

## Immutability and guardrails

<Warning>
  **Cannot change after deployment:**

  * The FeeManager address (set during Multi-Vehicle construction)
  * The `maxFees` ceiling (set during FeeManager deployment)

  **Plan carefully:** Choose `maxFees` conservatively. You can always charge less than the maximum, but you can never increase fees beyond it. For example, set `maxFees.performanceFeeBps = 3000` (30%) even if you start at 2000 (20%) to allow future flexibility.
</Warning>
