---
# System prepended metadata

title: VeYFI Subgraph APY/APR Research

---

# VeYFI Subgraph APY/APR Research

This research is realized as a spec here: https://hackmd.io/@l_6WjPP0TGCPZeo7re8_pw/ryuTspmd5

## Backward facing measures

Backwards facing APR/profit measurements need to factor in the following flow of rewards
```
Unrecognized rewards --->  Recognized Rewards    --->   Harvested Rewards
                      ^ caused by _updateReward   ^ caused by getReward
```
### Unrecognized Gauge Rewards

When it comes to graphing a user's unrecognized YFI rewards over time, it may be challenging to create a precise graph;. If a user locks veYFI + stakes vault tokens and does nothing else, we can only infer their unrecognized profit at time t=now by comparing `userRewardPerTokenPaid[account]` with `rewardPerTokenStored`. There is no way to figure out their unharvested profit between times t=(now-x) to time t=(now), where where t=(now-x) is the last time the user's rewards were updated via `_updateReward`. All we can do is interpolate.

#### Subgraph Spec

Note: the rewardPerToken/lastTimeRewardApplicable fields must be calculated offline or acquired via rpc because they are dependent on block.timestamp. They will be considered out of scope for the subgraph.

##### Gauge
Entity `Gauge` must have the following fields:
- rewardPerTokenStored (BigInt)
    - Subgraph must update on:
        - event UpdatedRewards
- periodFinish. Timestamp when current period will finish. (BigInt) 
    - Subgraph must update on:
        - event DurationUpdated
        - event RewardsAdded
- rewardRate (BigInt)
    - Subgraph must update on:
        - event DurationUpdated
        - event RewardsAdded
- totalSupply (BigInt)
    - Subgraph must update on:
        - event Staked
        - event Withdrawn
- lastUpdateTime (BigInt)
    - Subgraph must update on:
        - event RewardsAdded
        - event UpdatedRewards

##### GaugeAccount
Entity `GaugeAccount` must have the following fields:
- account (address)
- rewardPerTokenPaid (BigInt)
    - Basically a proxy of userRewardPerTokenPaid[account]
- maybe: lastUpdateRewardsBlock (BigInt)    (nullable)
- maybe: lastUpdateRewardsTimestamp (BigInt) (nullable)
- tokensStaked (BigInt)

rewardPerTokenPaid, lastUpdateRewardsBlock, and lastUpdateRewardsTimestamp would all be updated on the UpdatedRewards event.

#### SDK Spec

We only need to implement SDK support for unrecognized rewards if an account's unrecognized rewards need to be graphed over time. 

To obtain an account's total unharvested rewards at time t=now, the easiest way is to use an RPC request against `Gauge.earned(address)` .

If there is a need to model an account's unharvested rewards at a certain point in the past, we can revisit SDK support for unrecognized rewards. 


### Recognized Gauge Rewards

These are gauge rewards which have been "recognized" by the accounting in `_updateReward`. These rewards have not been paid out to the account.

New entity AccountRewardRecognized (on event UpdatedRewards & RewardPaid)
- Gauge
- Account
- Timestamp
- TimeSinceLastPayout
- NewRewardsAmount


### Harvested Gauge Rewards

These are gauge rewards that have been harvested by the account. For tax purposes, this is where gauge rewards ought to be "recognized" as income, so we need to include price information for YFI.

New entity UserRewardPaid (on RewardPaid)
- Gauge
- Account
- Timestamp
- AmountPaid
- Interval
- YfiPrice
- RewardDollarValue

### Extra notes

The need for a "Claimable YFI" measure may necessitate a ClaimableGaugeRewards entity

We will probably need a YFI Price entity if rewards need to be represented in $$$

Unharvested rewards can simply use the most recent YFI price.
Harvested rewards can use the price of YFI at the point at which the harvest went off. 


Going to need this: https://github.com/yearn/veYFI/issues/151
Otherwise, boostedBalance needs to be updated on the following events:
- Withdrawn
- Staked
- RewardPaid

If we want to calculate historical APY (gauge emissions as a % of vault tokens staked). We will need a historical price oracle for vault shares. Technically the yearn-vaults-v2 subgraph does this, but it would be much more convienent to reproduce it in the veYFI subgraph. 


## Forward facing measures

Forward facing APR calculations are much simpler to calculate thanks to the rewards being known ahead of time. At time t=now, we can predict the APR in the future up to t=now+n, where n is the amount of seconds remaining in the current rewards period. 
I recommend that the front end makes it clear that the gauge rewards APR is only accurate for the remaining reward period. It's totally possible for the gauge's next reward  period to have 0 rewards if no veYFI lockers vote for it. 

### Gauge Entity

Two new fields: 
 - aprBoostMaxLock
 - aprBoostNoLock

max boost apr:
```
gauge.rewardRate * secs_per_year * oracle.price(yfi)
-----------------------------------------------------
vault.totalAssets * oracle.price(vault.token)
```
We probably can get away with querying vault.totalAssets when we need to update apr's.

However: Do we need to store the result of the oracle query in the subgraph?

min boost apr:
```
 max boost apr * 0.10
```


## ExtraRewards

Out of scope for the moment



--- 
*note: this method is probably too complex*
A potentially more complex/precise option is to use the following pattern:

- Every time a user's boosting factor is adjusted, emit a UserBoostedBalanceUpdate entity that contains:
    - StartTimestamp
    - EndTimestamp
    - Account
    - Gauge
    - BoostedBalance
    - Multiplier?

- Every time a gauge's emission rate is updated, emit a GaugeEmissionRateUpdate entity that contains:
    - StartTimestamp
    - EndTimestamp
    - Gauge
    - EmissionRate

Frontend would request all UserBoostedBalanceUpdate over the past year, every GaugeEmissionRateUpdate over the past year for the gauge; todo: how would this handle UpdatedRewards?

###### tags: veyfi, subgraph, yearn