> For the complete documentation index, see [llms.txt](https://csm-docs.cozy.finance/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://csm-docs.cozy.finance/developer-guides/rewards-manager-accounting.md).

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