> ## 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.

# Additional rewards

> Capture and distribute extra yield from DeFi incentive programs

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

Additional rewards are incentive tokens distributed outside the normal yield flow by DeFi protocols. These rewards -- such as AAVE, COMP, MORPHO, or ENA tokens -- arrive via side channels and can significantly enhance user returns.

The key characteristic is that these rewards do not flow through the normal deposit/redeem lifecycle. They are distributed directly to vehicle addresses by external reward contracts and must be captured and distributed separately from base yield.

## The interceptor standard

The Interceptor standard is Railnet's solution for managing additional reward distribution. It provides a declarative on-chain configuration that off-chain indexing services read to correctly route rewards.

### How it works

<Steps>
  <Step title="Declare interception rules">
    The Vehicle declares distribution rules via the `interceptions()` view function.
  </Step>

  <Step title="Indexers query rules">
    Off-chain indexers (e.g., reward distributors, airdrop systems) read these rules to determine how to route rewards.
  </Step>

  <Step title="Rewards are distributed">
    The indexer applies the routing logic and distributes rewards according to the declared rules.
  </Step>
</Steps>

By keeping distribution logic off-chain, Railnet achieves gas efficiency (no on-chain overhead for reward splitting), flexibility (rules can be updated without migrating assets), and cross-chain support (rules can specify `chainId` targets).

### Core structures

<CodeGroup>
  ```solidity Solidity theme={null}
  struct Interception {
      address asset;              // Reward token (address(0) = all assets)
      Recipient[] recipients;     // Distribution rules
  }

  struct Recipient {
      address target;   // Where rewards go
      uint256 shareBps; // Share in basis points (10000 = 100%)
      uint256 chainId;  // Target chain (0 = all chains)
  }
  ```

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

**Key features:**

* **Asset-specific rules** -- Different routing for different reward tokens
* **Multi-recipient** -- Split rewards among multiple addresses
* **Cross-chain support** -- Route rewards to different chains
* **Pass-through** -- Unallocated rewards (where total `shareBps` \< 10,000) pass through to users
* **Last-matching-rule semantics** -- Later rules override earlier ones for the same asset, enabling powerful override patterns

### Matching semantics

Interceptors use **last-matching-rule** semantics, similar to CSS or firewall rules:

1. The indexer iterates through the `interceptions` array and selects the **last** entry that matches the reward token (either exactly or via the `address(0)` wildcard)
2. Within the selected rule, for each unique `target`, only the **last** valid entry matching the current `chainId` (or the `0` wildcard) is used

This allows you to define a default rule for all tokens, then append overrides for specific high-value tokens.

## Distribution strategies

<Tabs>
  <Tab title="Pass-through (100% to users)">
    All additional rewards flow directly to end users with no operator intervention.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Empty interceptions = 100% pass-through
      Interceptor.Interception[] memory interceptions =
          new Interceptor.Interception[](0);
      multiVehicle.setInterceptions(interceptions);
      ```

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

    **Best for:** Community-first strategies, DAOs with community governance, maximum transparency setups.
  </Tab>

  <Tab title="Fee collection (split)">
    The operator takes a percentage of rewards; the rest passes through to users.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Take 5% fee on all additional rewards
      Interceptor.Interception[] memory interceptions =
          new Interceptor.Interception[](1);
      interceptions[0] = Interceptor.Interception({
          asset: address(0),  // All reward tokens
          recipients: new Interceptor.Recipient[](1)
      });
      interceptions[0].recipients[0] = Interceptor.Recipient({
          target: operatorFeeAddress,
          shareBps: 500,   // 5%
          chainId: 0       // All chains
      });
      // Remaining 95% passes through to users

      multiVehicle.setInterceptions(interceptions);
      ```

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

    **Best for:** Professional strategy operators, sustainable long-term operations, institutional strategies.
  </Tab>

  <Tab title="Reinvestment (100% to operator)">
    The operator intercepts all rewards, sells them for the base asset, and reinvests to increase share price for all holders.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Intercept 100% of all additional rewards
      Interceptor.Interception[] memory interceptions =
          new Interceptor.Interception[](1);
      interceptions[0] = Interceptor.Interception({
          asset: address(0),
          recipients: new Interceptor.Recipient[](1)
      });
      interceptions[0].recipients[0] = Interceptor.Recipient({
          target: operatorRewardVault,
          shareBps: 10000,  // 100%
          chainId: 0
      });

      multiVehicle.setInterceptions(interceptions);
      ```

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

    **Best for:** Yield aggregators, single-asset strategies, operators with trading infrastructure.
  </Tab>
</Tabs>

## Reinvestment mechanics

When using the reinvestment strategy, the operator sells reward tokens and deposits the proceeds back into the Multi-Vehicle without minting new shares. This increases the share price for all holders.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Before reinvestment:
  // Total assets: 1,000,000 USDC
  // Total supply:  1,000,000 shares
  // Share price:   1.0 USDC/share

  // Rewards earned and sold:
  // 10,000 COMP -> 50,000 USDC
  // 5,000 AAVE  -> 100,000 USDC
  // Total to reinvest: 150,000 USDC

  // Operator deposits without minting shares:
  USDC.approve(address(sectorAccountingEngine), totalUsdc);
  sectorAccountingEngine.deposit(
      totalUsdc,
      false  // allocate=false: assets go to DEPOSIT sector
  );

  // After reinvestment:
  // Total assets: 1,150,000 USDC
  // Total supply:  1,000,000 shares (unchanged)
  // Share price:   1.15 USDC/share (+15%)
  ```

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

<Info>
  Calling `sectorAccountingEngine.deposit()` without minting shares increases `totalAssets` while keeping `totalSupply` constant. Since share price = `totalAssets / totalSupply`, all existing holders benefit proportionally.
</Info>

## Configuration

### Prerequisites

* A deployed Multi-Vehicle or Vehicle
* The `VEHICLE_SET_INTERCEPTIONS` role scoped to the Vehicle contract

### Asset-specific fees

Apply different fee rates to different reward tokens.

<CodeGroup>
  ```solidity Solidity theme={null}
  Interceptor.Interception[] memory interceptions = new Interceptor.Interception[](3);

  // Default: 5% on all tokens
  interceptions[0] = Interceptor.Interception({
      asset: address(0),
      recipients: new Interceptor.Recipient[](1)
  });
  interceptions[0].recipients[0] = Interceptor.Recipient({
      target: operatorFeeAddress,
      shareBps: 500,   // 5%
      chainId: 0
  });

  // AAVE rewards: 10% fee
  interceptions[1] = Interceptor.Interception({
      asset: address(AAVE_TOKEN),
      recipients: new Interceptor.Recipient[](1)
  });
  interceptions[1].recipients[0] = Interceptor.Recipient({
      target: operatorFeeAddress,
      shareBps: 1000,  // 10%
      chainId: 1       // Ethereum mainnet only
  });

  // COMP rewards: 3% fee
  interceptions[2] = Interceptor.Interception({
      asset: address(COMP_TOKEN),
      recipients: new Interceptor.Recipient[](1)
  });
  interceptions[2].recipients[0] = Interceptor.Recipient({
      target: operatorFeeAddress,
      shareBps: 300,   // 3%
      chainId: 0
  });

  multiVehicle.setInterceptions(interceptions);
  ```

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

### Multi-recipient split

Split fees between multiple addresses.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Split: 3% to operator, 2% to DAO treasury, 95% passes through to users
  Interceptor.Interception[] memory interceptions = new Interceptor.Interception[](1);
  interceptions[0] = Interceptor.Interception({
      asset: address(0),
      recipients: new Interceptor.Recipient[](2)
  });
  interceptions[0].recipients[0] = Interceptor.Recipient({
      target: operatorAddress,
      shareBps: 300,   // 3%
      chainId: 0
  });
  interceptions[0].recipients[1] = Interceptor.Recipient({
      target: daoTreasuryAddress,
      shareBps: 200,   // 2%
      chainId: 0
  });
  // Remaining 95% passes through to users

  multiVehicle.setInterceptions(interceptions);
  ```

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

### Hybrid fee + reinvestment

Take a small operator fee and reinvest the rest.

<CodeGroup>
  ```solidity Solidity theme={null}
  // 2% fee to operator, 98% reinvested for users
  Interceptor.Interception[] memory interceptions = new Interceptor.Interception[](1);
  interceptions[0] = Interceptor.Interception({
      asset: address(0),
      recipients: new Interceptor.Recipient[](2)
  });
  interceptions[0].recipients[0] = Interceptor.Recipient({
      target: operatorFeeAddress,
      shareBps: 200,    // 2% fee
      chainId: 0
  });
  interceptions[0].recipients[1] = Interceptor.Recipient({
      target: operatorRewardVault,
      shareBps: 9800,   // 98% reinvest
      chainId: 0
  });

  multiVehicle.setInterceptions(interceptions);
  ```

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

### Reinvestment workflow

After rewards accumulate at the operator reward vault:

<Steps>
  <Step title="Sell reward tokens for the base asset">
    Use a DEX aggregator (e.g. 1inch, Paraswap) to sell accumulated reward tokens for the Multi-Vehicle's base asset (e.g. USDC).
  </Step>

  <Step title="Deposit back into the Multi-Vehicle">
    Call `deposit()` on the Sector Accounting Engine with `allocate=false`. This increases `totalAssets` without minting new shares, raising the share price for all holders.

    **Requires:** `MULTI_VEHICLE_DEPOSIT` role scoped to the Sector Accounting Engine.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Approve and deposit reinvested rewards
      IERC20(usdc).approve(address(sectorAccountingEngine), totalRewards);
      sectorAccountingEngine.deposit(totalRewards, false);
      // allocate=false: assets go to the deposit sector as idle liquidity
      ```

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

  <Step title="Verify share price impact">
    After reinvestment, `totalAssets` increases while `totalSupply` stays the same, resulting in a higher share price.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Before reinvestment: totalAssets = 1,000,000, totalSupply = 1,000,000
      // Share price = 1.0

      // After reinvesting 150,000 USDC from rewards:
      // totalAssets = 1,150,000, totalSupply = 1,000,000
      // Share price = 1.15 (+15%)
      ```

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

## Advanced patterns

<AccordionGroup>
  <Accordion title="Governance token pass-through">
    Let users keep governance tokens while reinvesting yield tokens.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Default: Reinvest all
      interceptions[0] = Interceptor.Interception({
          asset: address(0),
          recipients: reinvestRecipients  // 100% to operator
      });

      // COMP: Pass through to users (for governance voting)
      interceptions[1] = Interceptor.Interception({
          asset: address(COMP_TOKEN),
          recipients: new Interceptor.Recipient[](0)  // 100% pass-through
      });

      // AAVE: Pass through to users
      interceptions[2] = Interceptor.Interception({
          asset: address(AAVE_TOKEN),
          recipients: new Interceptor.Recipient[](0)
      });
      ```

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

## Strategy comparison

| Strategy           | User experience              | Operator complexity           | Share price impact | Governance rights      |
| ------------------ | ---------------------------- | ----------------------------- | ------------------ | ---------------------- |
| **Pass-through**   | Users claim multiple tokens  | None                          | None               | Users keep tokens      |
| **Fee collection** | Users claim reduced amounts  | Low                           | None               | Users keep most tokens |
| **Reinvestment**   | Simple (share price grows)   | High (selling + redepositing) | Positive (+APY)    | Operator controls      |
| **Hybrid**         | Simple + small fee deduction | High                          | Positive           | Operator controls most |

## Required role

Updating interception rules requires the `VEHICLE_SET_INTERCEPTIONS` role:

<CodeGroup>
  ```solidity Solidity theme={null}
  multiVehicle.setInterceptions(interceptions);
  // Caller must have Roles.VEHICLE_SET_INTERCEPTIONS
  ```

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