Documentation

Additional Rewards & Interception Management

What Are Additional Rewards?

Additional rewards are incentive tokens distributed outside the normal yield flow by protocols to encourage usage. These rewards are distributed via side channels and can significantly enhance user returns.

Common Examples

  • AAVE: Receives stkAAVE rewards on deposits
  • Compound: Receives COMP token incentives
  • Morpho: Receives MORPHO reward tokens
  • Ethena: Receives ENA token airdrops
  • Liquidity Mining Programs: Various governance tokens

Key Characteristic

  • Rewards don’t flow through the normal deposit/redeem lifecycle
  • Distributed directly to vehicle addresses by external reward contracts
  • Must be claimed and distributed separately from base yield

The Interceptor Standard

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

How It Works

  1. Vehicle declares interception rules via interceptions() view function
  2. Off-chain indexers (e.g., reward distributors, airdrop systems) query these rules
  3. Rewards automatically routed according to declared rules
  4. No on-chain reward claiming needed - indexers handle distribution

Core Concepts

Interception Configuration:

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)
}

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 pass through to users
  • Dynamic updates: Change rules without redeploying

Configuration Function

Update interception rules on any vehicle:

multiVehicle.setInterceptions(Interceptor.Interception[] calldata interceptions);

Requirements:

  • Caller must have Roles.VEHICLE_SET_INTERCEPTIONS role

Use Case 1: Pass-Through (100% to Users)

Goal: All additional rewards flow directly to end users without any operator intervention.

When to use

  • Maximum user alignment
  • Simplest configuration
  • No operator reward management needed
  • Common for community-first strategies

Configuration

// Empty interceptions = 100% pass-through
Interceptor.Interception[] memory interceptions = new Interceptor.Interception[](0);
multiVehicle.setInterceptions(interceptions);

Result

  • AAVE rewards → flow to user addresses directly
  • COMP rewards → flow to user addresses directly
  • Any other rewards → flow to user addresses directly
  • No operator receives anything

Best for:

  • DAOs with community governance
  • Public goods strategies
  • Maximum transparency setups

Use Case 2: Fee Collection (Split Distribution)

Goal: Operator takes a small fee (e.g., 5%) on additional rewards, users receive the rest (95%).

When to use

  • Operator provides active management and deserves compensation
  • Sustainable business model for professional operators
  • Balance between operator incentives and user returns
  • Common for institutional strategies

Configuration

// 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,  // Operator's address
    shareBps: 500,               // 5% = 500 basis points
    chainId: 0                   // All chains
});

multiVehicle.setInterceptions(interceptions);

Asset-Specific Fee Example

// Different fees for different reward tokens
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 (higher because more valuable)
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 (lower to incentivize Compound usage)
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);

Multi-Recipient Split Example

// Split fees between operator (3%) and DAO treasury (2%)
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% to operator
    chainId: 0
});
interceptions[0].recipients[1] = Interceptor.Recipient({
    target: daoTreasuryAddress,
    shareBps: 200,  // 2% to DAO
    chainId: 0
});
// Remaining 95% passes through to users

multiVehicle.setInterceptions(interceptions);

Result

  • 1000 COMP earned → 50 COMP to operator, 950 COMP to users
  • 500 AAVE earned → 25 AAVE to operator, 475 AAVE to users
  • Operator can claim fees from their address
  • Users receive proportional shares automatically

Best for:

  • Professional strategy operators
  • Sustainable long-term operations
  • Transparent fee models

Use Case 3: Reinvestment Strategy (100% to Operator)

Goal: Operator intercepts 100% of additional rewards, sells them for base asset, and reinvests to increase share price for all holders.

When to use

  • Maximize compounding efficiency
  • Simplify user experience (no reward token claims)
  • Operator has infrastructure to sell rewards efficiently
  • Common for yield-focused strategies

Why This Works

  1. Calling sectorAccountingEngine.deposit() without minting shares increases totalAssets
  2. Share price = totalAssets / totalSupply
  3. Higher totalAssets with same totalSupply = higher share price
  4. All existing holders benefit proportionally (like a dividend to all shareholders)

Configuration

// Intercept 100% of 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: operatorRewardVault,  // Operator-controlled address for reward management
    shareBps: 10000,              // 100% = 10000 basis points
    chainId: 0                    // All chains
});

multiVehicle.setInterceptions(interceptions);

Operational Flow

1. Rewards accumulate at operatorRewardVault address

2. Operator monitors accumulated reward balances

3. Operator sells rewards for base asset (e.g., COMP → USDC, AAVE → USDC)

4. Operator reinvests by calling deposit() with allocate=false

// Operator workflow (off-chain bot or manual)
function reinvestRewards() external onlyOperator {
    // 1. Sell accumulated rewards for base asset
    uint256 compBalance = COMP.balanceOf(operatorRewardVault);
    uint256 aaveBalance = AAVE.balanceOf(operatorRewardVault);

    // Sell via DEX aggregator (e.g., 1inch, Paraswap)
    uint256 usdcFromComp = sellTokens(COMP, USDC, compBalance);
    uint256 usdcFromAave = sellTokens(AAVE, USDC, aaveBalance);

    uint256 totalUsdc = usdcFromComp + usdcFromAave;

    // 2. Approve and deposit to accounting engine
    USDC.approve(address(sectorAccountingEngine), totalUsdc);
    sectorAccountingEngine.deposit(
        totalUsdc,
        false  // allocate=false: assets go to the AVAILABLE sector (idle)
    );

    // 3. Optionally allocate immediately (or batch later)
    // ... manual allocation logic ...
}

Important Role Setup

// Operator needs MULTI_VEHICLE_DEPOSIT role to call deposit()
// grantRole takes (role, account). A scoped grant is grantScopedRole.
accessControl.grantScopedRole(
    Roles.MULTI_VEHICLE_DEPOSIT,
    address(sectorAccountingEngine),  // scope
    operatorAddress                   // grantee
);

Share Price Impact Example

Before Reinvestment:

Total assets: 1,000,000 USDC
Total supply: 1,000,000 shares
Share price: 1.0 USDC/share

Rewards Earned & Sold:

10,000 COMP earned, sold for 50,000 USDC
5,000 AAVE earned, sold for 100,000 USDC
Total to reinvest: 150,000 USDC

After Reinvestment:

Total assets: 1,150,000 USDC (1M + 150k)
Total supply: 1,000,000 shares (unchanged - no new minting)
Share price: 1.15 USDC/share (+15% from rewards)

User A with 10,000 shares:

Before: 10,000 shares × $1.0 = $10,000 value
After: 10,000 shares × $1.15 = $11,500 value (+$1,500 gain)

Result

  • Users receive value via increased share price (not via reward token claims)
  • Simpler UX (users don’t manage multiple reward tokens)
  • Operator handles complexity of selling/reinvesting
  • Transparent on-chain (totalAssets increases visible to all)

Best for:

  • Yield aggregators
  • Single-asset strategies
  • Professional operators with trading infrastructure
  • Users who prefer simplicity over reward token exposure

Advanced Configuration Patterns

Pattern 1: Hybrid Fee + Reinvestment

Goal: Take small fee (2%) and reinvest the rest (98%).

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
});
// Remaining 0% passes through (full interception)

multiVehicle.setInterceptions(interceptions);

Pattern 2: Governance Token Pass-Through, Others Reinvest

Goal: Let users keep governance tokens, operator reinvests yield tokens.

Interceptor.Interception[] memory interceptions = new Interceptor.Interception[](3);

// Default: Reinvest all
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
});

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

// AAVE: Pass through (users participate in AAVE governance)
interceptions[2] = Interceptor.Interception({
    asset: address(AAVE_TOKEN),
    recipients: new Interceptor.Recipient[](0)  // 0% interception = 100% pass-through
});

multiVehicle.setInterceptions(interceptions);

Pattern 3: Multi-Chain Reward Routing

Goal: Route mainnet rewards to operator, L2 rewards to users.

Interceptor.Interception[] memory interceptions = new Interceptor.Interception[](1);
interceptions[0] = Interceptor.Interception({
    asset: address(0),
    recipients: new Interceptor.Recipient[](2)
});

// Mainnet (chainId 1): Operator intercepts 100%
interceptions[0].recipients[0] = Interceptor.Recipient({
    target: operatorRewardVault,
    shareBps: 10000,  // 100%
    chainId: 1        // Mainnet
});

// All other chains: Pass through
interceptions[0].recipients[1] = Interceptor.Recipient({
    target: address(0),  // Dummy (will be ignored due to 0 shareBps)
    shareBps: 0,         // 0% = pass through
    chainId: 0           // Wildcard for other chains
});

multiVehicle.setInterceptions(interceptions);

Best Practices for Reward Management

1. Document Your Interception Strategy

Clearly communicate to users:

## Reward Distribution Policy

**Additional Rewards Handling:**
- AAVE/COMP/MORPHO governance tokens: 100% pass-through to users
- Other incentive tokens: 5% operator fee, 95% to users
- Operator fee funds ongoing strategy management and gas costs

**How to Claim:**
Users can claim their proportional rewards via [reward platform URL]

2. Regular Reinvestment Schedule

For reinvestment strategies:

  • Daily: High-volume strategies, high-value rewards
  • Weekly: Medium-volume strategies, moderate rewards
  • Monthly: Low-volume strategies, small rewards
// Example: Automated weekly reinvestment
function weeklyReinvest() external {
    require(block.timestamp >= lastReinvest + 7 days, "Too soon");

    uint256 totalRewards = estimateRewardValue();
    require(totalRewards >= minReinvestThreshold, "Rewards too small");

    // Execute reinvestment
    reinvestRewards();
    lastReinvest = block.timestamp;
}

3. Gas-Efficient Reward Sales

Considerations:

  • Use DEX aggregators (1inch, Paraswap) for best prices
  • Batch multiple reward tokens in single transaction
  • Set slippage limits to prevent sandwich attacks
  • Consider gas costs vs reward value
// Only reinvest if economical
uint256 estimatedGasCost = 200_000 * tx.gasprice * nativeTokenPrice / 1e18;
uint256 rewardValue = estimateRewardValue();

if (rewardValue > estimatedGasCost * 3) {  // 3x gas cost minimum
    reinvestRewards();
}

4. Monitor Interception Effectiveness

Track metrics:

  • Reward capture rate: % of distributed rewards actually received
  • Reinvestment frequency: Time between reward sale and redeposit
  • Share price impact: APY contribution from reinvested rewards
  • Fee collection: Revenue from intercepted fees

5. Transparent Reporting

Provide visibility to users:

## Monthly Reward Report (March 2025)

**Rewards Earned:**
- 12,500 COMP tokens ($156,250 value)
- 8,200 AAVE tokens ($737,000 value)
- Total: $893,250

**Distribution:**
- Operator Fee (5%): $44,662
- Reinvested (95%): $848,588
- Share Price Impact: +0.85%

**Next Reinvestment:** April 1, 2025

6. Update Interceptions for New Rewards

When protocols launch new incentives:

// Protocol announces new REWARDS_TOKEN distribution
// Update interceptions to include it

// Fetch current interceptions
Interceptor.Interception[] memory current = multiVehicle.interceptions();

// Add new reward token configuration
Interceptor.Interception[] memory updated = new Interceptor.Interception[](current.length + 1);
// ... copy existing + add new ...

multiVehicle.setInterceptions(updated);

7. Emergency Fee Adjustment

In extreme market conditions:

// During bear market: Reduce fees to keep users
Interceptor.Interception[] memory interceptions = new Interceptor.Interception[](1);
interceptions[0].recipients[0].shareBps = 200;  // Reduce from 5% to 2%

// During bull market: Increase fees (if governance approves)
interceptions[0].recipients[0].shareBps = 1000;  // Increase to 10%

Comparison of Strategies

StrategyUser ExperienceOperator ComplexityShare Price ImpactGovernance Rights
Pass-ThroughUsers claim multiple tokensNoneNoneUsers keep tokens
Fee CollectionUsers claim reduced amountsLow (just receive fees)NoneUsers keep 95%+
ReinvestmentSimple (share price grows)High (selling + redepositing)Positive (+APY)Operator controls

Recommendation by Strategy Type

  • Community DAO: Pass-Through (maximum alignment)
  • Yield Aggregator: Reinvestment (maximize APY)
  • Professional Manager: Fee Collection 3-10% (sustainable revenue)
  • Hybrid Strategy: 2% fee + reinvest remaining (balanced incentives)