Skip to content

Commit 8e9cd5d

Browse files
totekirobert-zarembaEbrahimElbagory
authored
docs: incentive module spec (#1949)
* docs: incentive module spec * suggestion++ Co-authored-by: Robert Zaremba <robert@zaremba.ch> * suggestion++ Co-authored-by: Robert Zaremba <robert@zaremba.ch> * suggestion++ Co-authored-by: Robert Zaremba <robert@zaremba.ch> * suggestion++ Co-authored-by: Robert Zaremba <robert@zaremba.ch> * su Co-authored-by: Robert Zaremba <robert@zaremba.ch> * No more tiers * -- * typo * ++ * Update x/incentive/README.md * Update x/incentive/README.md * lint * add example * add link * lint * lint * lint --------- Co-authored-by: Robert Zaremba <robert@zaremba.ch> Co-authored-by: Ebrahim <104380707+EbrahimUmee@users.noreply.github.com>
1 parent f989e7b commit 8e9cd5d

File tree

1 file changed

+161
-1
lines changed

1 file changed

+161
-1
lines changed

x/incentive/README.md

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,167 @@
44

55
This document specifies the `x/incentive` module of the Umee chain.
66

7-
The incentive module allows users to `Bond` collateral `uTokens` from the `x/leverage` module, and governance to create and fund `Incentive Programs` which distribute rewards to users with bonded uTokens over time. Users can `Unbond` tokens over a period of time, after which they can be withdrawn.
7+
The incentive module allows users to `Bond` collateral `uTokens` from the `x/leverage` module, and governance to create and fund `Incentive Programs` which distribute rewards to users with bonded uTokens over time.
8+
9+
Users can `Unbond` tokens over a period of time, after which they can be withdrawn. `UnbondingDuration` is a module parameter (the same for all tokens) set by governance. Typical values might be `1 day`, `7 days`, or `0 days` (instant unbonding).
810

911
The incentive module depends on the `x/leverage` module for information about users' bonded collateral, and also requires that the leverage module prevent bonded or currently unbonding collateral from being withdrawn.
1012
There are also a few more advanced interactions, such as instantly unbonding collateral when it is liquidated, and registering an `exponent` when a uToken denom is incentivized for the first time.
13+
14+
## Contents
15+
16+
1. **[Concepts](#concepts)**
17+
- [Bonding Collateral](#bonding-collateral)
18+
- [Incentive Programs](#incentive-programs)
19+
- [Claiming Rewards](#claiming-rewards)
20+
- [Reward Accumulators](#reward-accumulators)
21+
- [Reward Trackers](#reward-trackers)
22+
2. **[State](#state)**
23+
3. **[Messages](#messages)**
24+
25+
## Concepts
26+
27+
### Bonding Collateral
28+
29+
A user can bond their `x/leverage` collaterized `uTokens` in a `x/incentive` module to receive extra rewards.
30+
31+
Bonding prevents the user from using any `leverage.MsgDecollateralize` or `leverage.MsgWithdraw` which would reduce the user's collateral below the bonded amount.
32+
33+
**Example:** a user has `100 u/UMEE` and `50 u/UMEE` collateral in the leverage module. `40 u/UMEE` from that `50 u/UMEE` is bonded in the incentive module. Their maximum `leverage.MsgDecollateralize` allowed by their bond is `10 u/UMEE` and their maximum `leverage.MsgWithdraw` is `110u/UMEE`.
34+
35+
Bonded collateral is eligible for incentive program rewards as long as it is not currently unbonding.
36+
37+
When the user starts unbonding a uToken, the module's `UnbondingDuration` determines the time after which the tokens are unlocked to the user.
38+
39+
Unbonding uTokens are not eligible for incentive rewards while they unbond, but are still subject to the same restrictions on `leverage.MsgWithdraw` and `leverage.MsgDecollateralize` as bonded tokens.
40+
41+
For example, if a user has `10u/UMEE` bonded and `3u/UMEE` more unbonding, out of a total of `20u/UMEE` collateral, then their current max withdraw is `7u/UMEE`, and they are earning incentive rewards on only `10u/UMEE` uTokens.
42+
43+
The module parameter `MaxUnbondings` limits how many concurrent unbondings a user can have of the same uToken denom, to prevent spam.
44+
45+
Additionally, `MsgEmergencyUnbond` can instantly unbond collateral, starting with in-progress unbondings then bonded tokens.
46+
This costs a fee - for example, if the parameter `EmergencyUnbondFee` is `0.01`, then 1% of the uTokens unbonded would be donated to the `x/leverage` module reserves while the other 99% are returned to the user.
47+
48+
### Incentive Programs
49+
50+
An `IncentiveProgram` is a fixed-duration program which distributes a predetermined amount of one reward token to users which have bonded selected uTokens during its duration.
51+
52+
For example, the following incentive program would, at each block during the `864000 seconds` after its start at `unix time 1679659746`, distribute a portion of its total `1000 UMEE` rewards to users who have bonded (but are not currently unbonding) `u/uumee` to the incentive module.
53+
54+
```go
55+
ip := IncentiveProgram {
56+
StartTime: 1679659746, // Mar 24 2023
57+
Duration: 864000, // 10 days
58+
UToken: "u/uumee",
59+
TotalRewards: sdk.Coin{"uumee",1000_000000},
60+
}
61+
```
62+
63+
Reward distribution math is
64+
65+
- **Constant Rate:** Regardless of how much `u/uumee` is bonded to the incentive module at any given block, the program distributes the fraction `blockTime / duration` of its total rewards across users every block it is active (with some corrective rounding).
66+
- **Weighted by Amount:** A user with `200u/uumee` bonded received twice as many rewards on a given block as a user with `100u/uumee` bonded.
67+
68+
When multiple incentive programs are active simultaneously, they compute their rewards independently.
69+
70+
### Claiming Rewards
71+
72+
A user can claim rewards for all of their bonded uTokens at once using `MsgClaim`. When a user claims rewards, an appropriate amount of tokens are sent from the `x/incentive` module account to their wallet.
73+
74+
There are also times where rewards must be claimed automatically to maintain rewards-tracking math. These times are:
75+
76+
- On `MsgBond`
77+
- On `MsgBeginUnbonding`
78+
- When a `leverage.MsgLiquidate` forcefully reduces the user's bonded collateral
79+
80+
Any of the actions above cause the same tokens to be transferred to the user as would have been generated by a `MsgClaim` at the same moment.
81+
82+
By automatically claiming rewards whenever a user's bonded amount changes, the module guarantees the following invariant:
83+
84+
> Between any two consecutive reward claims by an account associated with a specific bonded uToken denom, the amount of bonded `uTokens` of the given denom for that account remained constant.
85+
86+
### Reward Accumulators
87+
88+
The incentive module must calculate rewards each block without iterating over accounts and past incentive programs. It does so by storing a number of `RewardAccumulator`
89+
90+
```go
91+
ra := RewardAccumulator{
92+
denom: "u/uumee",
93+
exponent: 6,
94+
rewards: "0.00023uumee, 0.00014ibc/1234",
95+
}
96+
```
97+
98+
The example reward accumulator above can be interpreted as:
99+
100+
> If `10^6` `u/uumee` was bonded at genesis and had remained bonded since, it would have accumulated `0.00023uumee` and `0.00014ibc/1234` in rewards.
101+
102+
The incentive module must store one `RewardAccumulator` for each uToken denom that is currently (or has been previously) incentivized.
103+
104+
During `EndBlock` when rewards are being distributed by incentive programs, the module divides the amount of tokens to distribute by the current `TotalBonded` which it stores for each uToken denom, to increment the `rewards` field in each `RewardAccumulator`.
105+
106+
The `exponent` field is based on the exponent of the base asset associated with the uToken in `denom`. It reduces the loss of precision that may result from dividing the (relatively small) amount of rewards distributed in a single block by the (relatively large) total amount of uTokens bonded.
107+
108+
Note that the `rewards` field must never decrease, nor can any nonzero `RewardAccumulator` be deleted, even after associated incentive programs have ended. The `exponent` field should also never be changed.
109+
110+
### Reward Trackers
111+
112+
`RewardTracker` is stored per-user, and is used in combination with `RewardAccumulator` to calculate rewards since the user's last claim.
113+
114+
```go
115+
rt := RewardTracker{
116+
account: "umee1s84d29zk3k20xk9f0hvczkax90l9t94g72n6wm",
117+
denom: "u/uumee",
118+
rewards: "0.00020uumee, 0.00014ibc/1234",
119+
}
120+
```
121+
122+
The example reward tracker above can be interpreted as:
123+
124+
> The last time account `umee1s84d29zk3k20xk9f0hvczkax90l9t94g72n6wm` claimed rewards for bonded `u/uumee`, the value of `RewardAccumulator` (not tracker) for that denom was `"0.00020uumee, 0.00014ibc/1234"`. Therefore, the rewards to claim this time should be based on the _increase_ since then.
125+
126+
Because the amount of bonded uTokens for this user was constant between the previous `RewardTracker` increase and the current moment, the following simple calculation determines rewards:
127+
128+
> rewards to claim = (RewardAccumulator - RewardTracker) * (AmoundBonded / 10^Exponent)
129+
130+
Whenever an account claims rewards (including the times when rewards are claimed automatically due to `MsgBond`, `MsgBeginUnbonding`, or `leverage.MsgLiquidate`), the account's `RewardTracker` of the affected denom must be updated to the current value of `RewardAccumulator`.
131+
132+
A `RewardTracker` can also be deleted from the module's store once an account's bonded amount has reached zero for a uToken `denom`. The tracker will be initialized to the accumulator's current value if the account decides to bond again later.
133+
134+
## State
135+
136+
The incentive module stores the following in its KVStore and genesis state:
137+
138+
- `Params`
139+
- Every upcoming, ongoing, and completed `IncentiveProgram`
140+
- The next available program `ID` (an integer)
141+
- The last unix time rewards were calculated
142+
- `RewardAccumulator` of every bonded uToken denom, unless zero
143+
- `RewardTracker` for each user associated with nonzero bonded uTokens with a nonzero a reward accumulator above
144+
- All nonzero bonded uTokens amounts for each account (excldes unbondings)
145+
- All unbondings in progress for each account
146+
147+
Additionally, some mathematically redundant information is maintained in `KVStore` but not genesis state for efficient operations:
148+
149+
- `TotalBonded` for every bonded uToken denom (total excludes unbondings).
150+
- `TotalUnbonding` for every bonded uToken denom.
151+
- `AmountUnbonding` for each account with one or more unbondings in progress.
152+
153+
These totals are kept in sync with the values they track by the functions in `keeper/store.go`, which update the totals whenever any of the values they reference are changed for any reason (including when importing genesis state).
154+
155+
## Messages
156+
157+
See [proto](https://github.com/umee-network/umee/blob/main/proto/umee/incentive/v1/tx.proto) for detailed fields.
158+
159+
Permissionless:
160+
161+
- `MsgClaim` Claims any pending rewards for all bonded uTokens
162+
- `MsgBond` Bonds uToken collateral (and claims pending rewarda)
163+
- `MsgBeginUnbonding` Starts unbonding uToken collateral (and claims pending rewards)
164+
- `MsgEmergencyUnbond` Instantly unbonds uToken collateral for a fee based on amount unbonded (and claims pending rewards)
165+
- `MsgSponsor` Funds an entire incentive program with rewards, if it has been passed by governance but not yet funded.
166+
167+
Governance controlled:
168+
169+
- `MsgGovSetParams` Sets module parameters
170+
- `MsgGovCreatePrograms` Creates one or more incentive programs. These programs can be set for community funding or manual funding in the proposal.

0 commit comments

Comments
 (0)