Asynchronous Reward Compounding

How reward claiming and compounding work when they can't happen atomically with a strategy action.

In sophisticated DeFi strategies, yield is often composed of Synchronous Yield (intrinsic interest accrual) and Asynchronous Rewards (external incentives). For an Atomist curating a Fusion Vault, the temporal gap between earning a reward and compounding it into the vault's totalAssets() creates a dynamic where the share price may temporarily deviate from the strategy's "true" economic value.

This gap results in one of two effects depending on TVL fluctuations: Dilution or Concentration.

The Compounding Gap

Synchronous Accrual

Base lending protocols (like Aave or Morpho) usually provide synchronous yield. When a Fusion Vault calls updateMarketsBalances(), the vault's share price immediately reflects the interest earned or the costs incurred (e.g., borrowing fees).

Asynchronous Accrual

Many incentive programs (e.g., $ARB, $OP, or $MORPHO) accrue rewards on external distributor contracts. These assets are "invisible" to the vault's exchange rate until the Atomist:

  1. Claims tokens via the RewardsClaimManager.
  2. Converts them to the underlying asset via the UniversalTokenSwapper.
  3. Re-deposits them, effectively increasing totalAssets().

Temporal Effects: Inflow vs. Outflow

Because the share price only accounts for assets currently tracked within the vault's storage, the value of pending (unclaimed) rewards exists "off-balance-sheet."

1. Reward Dilution (Capital Inflow)

If a large deposit occurs after a reward has been earned but before it is compounded, the new capital receives a pro-rata share of rewards it did not help earn. This is particularly noticeable in leveraged strategies where the vault has already realized the synchronous cost of the leverage (borrowing fees) but has not yet realized the asynchronous upside (incentives).

2. Reward Concentration (Capital Outflow)

Conversely, if a significant portion of the TVL is withdrawn before the compounding point, the remaining shareholders benefit from "positive dilution." The rewards earned by the total capital are now distributed among a smaller pool of shares, making the compounding event accretive to the remaining users.

Technical Example: 7-Day Compounding Cycle

Consider a Fusion Vault utilizing a leveraged supply strategy.

Initial State:

  • TVL: 1,000,000 USDC
  • Total Supply: 1,000,000 shares (Price: 1.0000)
  • Strategy: 3% APY borrow cost to farm a reward token yielding 5% APY.

Accrual Period (7 Days):

The vault maintains the position for one week.

Synchronous Cost (C):

C=Assets×Rateborrow×Days365=1,000,000×0.03×7365≈575.34C = \frac{Assets \times Rate_{borrow} \times Days}{365} = \frac{1,000,000 \times 0.03 \times 7}{365} \approx 575.34

Asynchronous Reward (R):

R=Assets×Ratereward×Days365=1,000,000×0.05×7365≈958.90R = \frac{Assets \times Rate_{reward} \times Days}{365} = \frac{1,000,000 \times 0.05 \times 7}{365} \approx 958.90

State at Day 7 (Pre-Compounding):

The borrow cost is realized via updateMarketsBalances(), but the reward is pending.

  • Total Assets: 999,424.66 USDC
  • Share Price: 0.99942

Scenario A: Large Inflow (Dilution)

A user deposits 4,000,000 USDC right before compounding.

  • New Total Supply: 1,000,000+(4,000,000/0.99942)≈5,002,3211,000,000 + (4,000,000 / 0.99942) \approx 5,002,321 shares.
  • Post-Compounding Price: After adding the 958.90 USDC reward:

Price=4,999,424.66+958.905,002,321≈1.00001Price = \frac{4,999,424.66 + 958.90}{5,002,321} \approx 1.00001

Note: The original users lose nearly all their net yield to the new capital inflow.

Scenario B: Large Outflow (Concentration)

Users withdraw 500,000 USDC right before compounding.

  • New Total Supply: 500,000 shares remaining.
  • Post-Compounding Price: After adding the 958.90 USDC reward to the remaining 499,424.66 USDC assets:

Price=499,424.66+958.90500,000≈1.00076Price = \frac{499,424.66 + 958.90}{500,000} \approx 1.00076

Note: The remaining users receive a higher share price because they capture the rewards earned by the capital that just exited.

Strategic Management for Atomists

Atomists should manage the compounding gap to ensure fairness and strategy stability.

1. Compounding Frequency

Increasing the frequency of reward realization (e.g., daily instead of weekly) minimizes the window for both dilution and concentration, keeping the share price closer to the strategy's fair value.

If rewards can be claimed at specific time intervals, the compounding period should correspond to these intervals. Otherwise, fluctuations in the share price can occur, which could be exploited through timed depositing and withdrawing, leading to yield leakage from the vault.

2. Onboarding Contributions

The FeeManager can be used to set a Onboarding Contribution. This fee acts as an "entry contribution" that can offset the dilution of existing shareholders, effectively pricing in the value of pending rewards for new participants.

3. Scaling and Velocity

Atomists should monitor TVL Velocity. Strategies with high asynchronous components should be scaled carefully. Sudden spikes in TVL can be managed using setTotalSupplyCap(), allowing the vault to grow in stages that align with compounding cycles.

4. Vesting Claimed Rewards

Compounding a claimed reward straight into the vault adds it to totalAssets() in one block, so the share price jumps at every claim. A depositor who enters just before a claim and exits right after captures part of a reward the vault earned over the whole claim interval. The RewardsClaimManager removes the jump: it releases claimed rewards into totalAssets() linearly over a vesting period the Atomist sets.

How vesting works

  • The vault counts RewardsClaimManager.balanceOf() in totalAssets(). With a vesting time of 0, that is the manager's whole balance of the vault's underlying asset. With a vesting time above 0, it is only the part vested so far.
  • updateBalance() moves the vested part to the vault and starts a new linear vest of everything the manager still holds, over the full vesting time.
  • Only the vault's underlying asset vests. Other tokens in the manager are not counted in totalAssets() until they are converted to the underlying asset and sent back to the manager.
  • transferVestedTokensToVault() is public. It moves the vested part to the vault without changing totalAssets().

Roles

FunctionRoleWho holds it
setupVestingTime(seconds)ATOMIST_ROLE (100)Atomist
claimRewards(...)CLAIM_REWARDS_ROLE (600)Alpha
transfer(token, to, amount)TRANSFER_REWARDS_ROLE (700)Alpha or a conversion account; cannot move the underlying asset
updateBalance()UPDATE_REWARDS_BALANCE_ROLE (1100)Alpha

The Atomist is the admin of roles 600, 700 and 1100 and grants them in the vault's IporFusionAccessManager.

Setup

  1. Set the vesting time with setupVestingTime. Match it to the claim interval: if rewards are claimed every 36 hours, vest over about 36 to 48 hours. A longer period gives a smoother share price but delays the reward, and depositors who exit leave the unvested part to those who stay.
  2. Grant TRANSFER_REWARDS_ROLE and UPDATE_REWARDS_BALANCE_ROLE to the Alpha (or the account that converts rewards).

Each claim cycle

  1. Claim through RewardsClaimManager.claimRewards. Claim fuses send the claimed tokens to the manager. For MerklClaimFuse, keep the reward token out of the doNotTransferToRewardManager list; a token on that list stays in the vault and is compounded at once. For wrapped Merkl tokens use MerklClaimWrapperFuse (see Checks).
  2. If the reward token is not the underlying asset, transfer it to the conversion account, convert it to the underlying asset, and send the proceeds back to the RewardsClaimManager address.
  3. Call updateBalance(). The new balance starts vesting.

Checks

  • getVestingData() returns vestingTime, updateBalanceTimestamp (0 means vesting never started), transferredTokens and lastUpdateBalance.
  • setupVestingTime can revert in the middle of a vest when the new time would cut the already-released amount. Call updateBalance() first, then change the time.
  • MerklClaimFuse forwards the balance change of the token named in the claim. Some Merkl reward tokens are wrappers that unwrap on transfer and pay out a different token (for example a wrapped Aave aToken that delivers the aToken itself). With MerklClaimFuse the claimed token's balance does not change and nothing reaches the manager. Use MerklClaimWrapperFuse for these tokens: its claim takes the list of tokens that actually arrive (receivedTokens), measures their balance change and forwards it to the manager. Each received token must be granted as a substrate-as-asset on the fuse's MARKET_ID, and the fuse must be added to the manager with addRewardFuses.
  • Rewards held in the conversion account are outside the vault and are not counted in totalAssets() until they come back to the manager. Keep the round trip short.

Last updated