# Rewards Manager Accounting

The graphic below provides a useful mental model of how rewards accounting works.

Conceptually, rewards flow from the `RewardPool` struct, to `ClaimableRewardsData` structs (one for each stake pool), to `UserRewardsData` (one for each staker in a stake pool).

<figure><img src="/files/PFeh6vc2xy48FqjWqgSW" alt="" width="563"><figcaption></figcaption></figure>

### Reward Pools and Drip

The `RewardPool` struct stores undripped rewards and cumulative dripped rewards. At this stage, there is no concept of stake pools or stakers.&#x20;

```solidity
struct RewardPool {
  // The amount of undripped rewards held by the reward pool.
  uint256 undrippedRewards;
  // The cumulative amount of rewards dripped since the last config update. This value is reset to 0 on each config
  // update.
  uint256 cumulativeDrippedRewards;
  // The last time undripped rewards were dripped from the reward pool.
  uint128 lastDripTime;
  ...
}
```

#### When do rewards drip?

Rewards can either drip simultaneously for all reward pools or for a single reward pool.&#x20;

Many operations in the Rewards Manager internally drip rewards. However, anyone can drip rewards on-demand by calling `RewardsManager.dripRewards` and `RewardsManager.dripRewardPool`, which are also public and external functions, respectively.

The following operations internally drip rewards for all reward pools:

* `RewardsManager.pause`
* `RewardsManager.unpause`
* `RewardsManager.claimRewards`
* `RewardsManager.updateConfigs`
* `RewardsManager.dripRewards`
* `RewardsManager.stake`
* `RewardsManager.stakeWithoutTransfer`

The following operations internally drip rewards for a single reward pool:

* `RewardsManager.deposit`
* `RewardsManager.depositWithoutTransfer`
* `RewardsManager.dripRewardPool`

#### How exactly does drip work?

The core drip functionality is implemented in `RewardsManager._dripRewardPool`. When a reward pool drips, the following happens:

* `undrippedRewards` gets decremented by the amount of dripped rewards.
* `cumulativeDrippedRewards` gets incremented by that same amount of dripped rewards.
* `lastDripTime` updates to `block.timestamp`.

The amount of dripped rewards is simply:

<pre><code><strong>dripFactor = dripModel.dripFactor(lastDripTime);
</strong><strong>drippedRewards = dripFactor * rewardPool.undrippedRewards;
</strong></code></pre>

### Claimable Rewards

The `claimableRewards` storage variable is a nested mapping which maps stake pool IDs to reward pool IDs to a `ClaimableRewardsData` struct.&#x20;

Each `ClaimableRewardsData` struct is used to track the claimable rewards associated with a given (stake pool, reward pool) pair.

```solidity
struct ClaimableRewardsData {
  // The cumulative amount of rewards that are claimable on behalf of all users. This value is reset to 0 on each
  // config update.
  uint256 cumulativeClaimableRewards;
  // The index snapshot the relevant claimable rewards data, when the cumulative claimed rewards were updated. The index
  // snapshot must update each time the cumulative claimed rewards are updated.
  uint256 indexSnapshot;
}
```

#### When do claimable rewards get updated?

The following operations trigger an update to `claimableRewards`:

* `RewardsManager.claimRewards` -> `_claimRewards`
* `RewardsManager.stake` and `RewardsManager.stakeWithoutTransfer` -> `_dripAndApplyPendingDrippedRewards`
* `RewardsManager.unstake` -> `_claimRewards`
* `RewardsManger.updateConfigs` -> `_dripAndResetCumulativeRewardsValues`

#### How exactly do claimable rewards work?

The `claimableRewards` variable is lazily updated, which obviates the need to iterate through all (stake pool, reward pool) pairs when unnecessary.

The `cumulativeClaimableRewards` value is the amount of `RewardPool.cumulativeDrippedRewards` which are now fully "claimable" by stakers in a specific stake pool. Since it is lazily updated, it is a lagging value. Specifically, we have the following invariant always holds:

```
claimableRewards[stakePoolId][rewardPoolId].cumulativeClaimableRewards <=
    rewardPools[rewardPoolId].cumulativeDrippedRewards.mulDivDown(stakePools[stakePoolId].rewardsWeight, ZOC)
```

Each time `cumulativeClaimableRewards` is updated, it is brought in sync with the right-hand side of the inequality above.

Each time `cumulativeClaimableRewards` is updated, so is `indexSnapshot`. The `indexSnapshot` value represents the accrued rewards of a theoretical staker who owns a single `stkReceiptToken` and began staking at initialization of the stake pool.&#x20;

Say `cumulativeClaimableRewards` is incremented by `x`. Then:

```
indexSnapshot += x.divWadDown(stakePools[stakePoolId].stkReceiptToken.totalSupply())
```

### User Rewards

The `userRewards` storage variable is a nested mapping which maps stake pool IDs to staker addresses to an array of `UserRewardsData` structs. Each `UserRewardsData` struct is used to track the rewards a staker is entitled to for a given reward pool.

```solidity
struct UserRewardsData {
  // The total amount of rewards accrued by the user.
  uint256 accruedRewards;
  // The index snapshot the relevant claimable rewards data, when the user's accrued rewards were updated. The index
  // snapshot must update each time the user's accrued rewards are updated.
  uint256 indexSnapshot;
}
```

#### When do user rewards get updated?

Any operation which updates a user's `stkReceiptToken` balance or claims some of the user's accrued rewards triggers an update to `userRewards`:

* `RewardsManager.claimRewards` -> `_claimRewards`
* `RewardsManager.stake` and `RewardsManager.stakeWithoutTransfer` -> `_updateUserRewards`
* `RewardsManager.unstake` -> `_claimRewards`
* `StkReceiptToken.transfer` -> `_updateUserRewardsForStkReceiptTokenTransfer`

#### How exactly do user rewards work?

Much like claimable rewards, `accruedRewards` is lazily updated since it is impossible to iterate through all stakers in a given stake pool.

Each time a user's `stkReceiptToken` balance changes, `accruedRewards` are updated as follows:

<pre><code><strong>oldRewardPoolIndex = userRewards[stakePoolId][staker][rewardPoolId].indexSnapshot
</strong><strong>newRewardPoolIndex = claimableRewards[stakePoolId][rewardPoolId].indexSnapshot
</strong><strong>accruedRewards += stakerReceiptTokenBalance.mulWadDown(newRewardPoolIndex - oldRewardPoolIndex);
</strong></code></pre>

The special case is when the rewards are claimed. In that case, `accuredRewards` is set to 0.

Whenever `accruedRewards` is updated, the user rewards `indexSnapshot` is also brought in sync with the claimable rewards `indexSnapshot`.

Since new reward pools can be added after a user has staked, it is possible that `userRewards[stakePoolId][staker].length <= rewardPools.length`. In that case, there is special handling to push a new `UserRewardsData` struct.

### Config Updates and Claimable Rewards

Recall that the claimable rewards accounting crucially depends on an [invariant](#how-exactly-do-claimable-rewards-work), which uses the `StakePool.rewardsWeight`.&#x20;

It is possible that a Rewards Manager config update changes these weights and the invariant no longer holds. So, before any config update is applied, all claimable rewards data is fully reset. More specifically:

* All reward pools are dripped.
* `ClaimableRewardsData.indexSnapshot` is fully updated for all (stake pool, reward pool) pairs.
* `RewardPool.cumulativeDrippedRewards` is reset to 0.
* `ClaimableRewardsData.cumulativeClaimedRewards` is reset to 0.

By resetting the cumulative rewards values to 0, we can use the invariant again to do accounting until there is another config update.&#x20;

### StkReceiptToken Transfers

Stake receipt token transfers change user balances and so must get reflected in the Rewards Manager's user rewards accounting.

Prior to the standard `IERC20.transfer` call, `stkReceiptToken`s call `RewardsManager.updateUserRewardsForStkReceiptTokenTransfer`. This function  brings the `UserRewardsData` for both the `to` and `from` address up to date, so rewards accrue properly after the transfer occurs.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://csm-docs.cozy.finance/developer-guides/rewards-manager-accounting.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
