add incentive spec

This commit is contained in:
Kevin Davis 2020-05-07 13:46:48 -04:00
parent a295b793a9
commit c1e9a87fb3
7 changed files with 145 additions and 0 deletions

View File

@ -0,0 +1,5 @@
# Concepts
This module presents an implementation of user incentives that are controlled by governance. When users take a certain action, in this case opening a CDP, they become eligible for rewards. Rewards are __opt in__ meaning that users must submit a message before the claim deadline to claim their rewards. The goals and background of this module were subject of a previous Kava governance proposal, which can be found [here](https://ipfs.io/ipfs/QmSYedssC3nyQacDJmNcREtgmTPyaMx2JX7RNkMdAVkdkr/user-growth-fund-proposal.pdf).
When governance adds a collateral type to be eligible for rewards, they set the rate (coins/time) at which rewards are given to users, the length of each reward period, the length of each claim period, and the amount of time reward coins must vest before users who claim them can transfer them. For the duration of a reward period, any user that has minted USDX using an eligible collateral type will ratably accumulate rewards in a `Claim` object. For example, if a user has minted 10% of all USDX for the duration of the reward period, they will earn 10% of all rewards for that period. When the reward period ends, the claim period begins immediately, at which point users can submit a message to claim their rewards. Rewards are time-locked, meaning that when a user claims rewards they will receive them as a vesting balance on their account. Vesting balances can be used to stake coins, but cannot be transferred until the vesting period ends.

View File

@ -0,0 +1,57 @@
# State
## Parameters and Genesis State
`Parameters` define the collateral types which are eligible for rewards, the rate at which rewards are given to users, and the amount of time rewards must vest before users can transfer them.
```go
// Params governance parameters for the incentive module
type Params struct {
Active bool `json:"active" yaml:"active"` // top level governance switch to disable all rewards
Rewards Rewards `json:"rewards" yaml:"rewards"`
}
// Reward stores the specified state for a single reward period.
type Reward struct {
Active bool `json:"active" yaml:"active"` // governance switch to disable a period
Denom string `json:"denom" yaml:"denom"` // the collateral denom rewards apply to, must be found in the cdp collaterals
AvailableRewards sdk.Coin `json:"available_rewards" yaml:"available_rewards"` // the total amount of coins distributed per period
Duration time.Duration `json:"duration" yaml:"duration"` // the duration of the period
TimeLock time.Duration `json:"time_lock" yaml:"time_lock"` // how long rewards for this period are timelocked
ClaimDuration time.Duration `json:"claim_duration" yaml:"claim_duration"` // how long users have after the period ends to claim their rewards
}
```
`GenesisState` defines the state that must be persisted when the blockchain stops/restarts in order for normal function of the incentive module to resume.
```go
// GenesisState is the state that must be provided at genesis.
type GenesisState struct {
Params Params `json:"params" yaml:"params"`
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
RewardPeriods RewardPeriods `json:"reward_periods" yaml:"reward_periods"`
ClaimPeriods ClaimPeriods `json:"claim_periods" yaml:"claim_periods"`
Claims Claims `json:"claims" yaml:"claims"`
NextClaimPeriodIDs GenesisClaimPeriodIDs `json:"next_claim_period_ids" yaml:"next_claim_period_ids"`
}
```
## Store
For complete details for how items are stored, see [keys.go](../types/keys.go).
### Reward Period Creation
At genesis, or when a collateral is added to rewards, a `RewardPeriod` is created in the store by adding to the existing array of `[]RewardPeriod`. If the previous period for that collateral expired, it is deleted. This implies that, for each collateral, there will only ever be one reward period.
### Reward Period Deletion
When a `RewardPeriod` expires, a new `ClaimPeriod` is created in the store with the next sequential ID for that collateral (ie, if the previous claim period was ID 1, the next one will be ID 2) and the current `RewardPeriod` is deleted from the array of `[]RewardPeriod`.
### Reward Claim Creation
Every block, CDPs are iterated over and the collateral denom is checked for rewards eligibility. For eligible CDPs, a `RewardClaim` is created in the store for all CDP owners, if one doesn't already exist. The reward object is associated with a `ClaimPeriod` via the ID. This implies that `RewardClaim` are created before `ClaimPeriod` are created. Therefore, a user who submits a `MsgClaimReward` will only be paid out IF 1) they have one or more active `RewardClaim` objects, and 2) if the `ClaimPeriod` with the associated ID for that object exists AND the current block time is between the start time and end time for that `ClaimPeriod`.
### Reward Claim Deletion
For claimed rewards, the `RewardClaim` is deleted from the store by deleting the key associated with that denom, ID, and owner. Unclaimed rewards are handled as follows: Each block, the `ClaimPeriod` objects for each denom are iterated over and checked for expiry. If expired, all `RewardClaim` objects for that ID are deleted, as well as the `ClaimPeriod` object. Since claim periods are monotonically increasing, once a non-expired claim period is reached, the iteration can be stopped.

View File

@ -0,0 +1,16 @@
# Messages
Users claim rewards using a `MsgClaimReward`.
```go
// MsgClaimReward message type used to claim rewards
type MsgClaimReward struct {
Sender sdk.AccAddress `json:"sender" yaml:"sender"`
Denom string `json:"denom" yaml:"denom"`
}
```
## State Modifications
* Accumulated rewards for active claims are transferred from the `kavadist` module account to the users account as vesting coins
* The corresponding claim object(s) are deleted from the store

View File

@ -0,0 +1,20 @@
# Events
The `x/incentive` module emits the following events:
## MsgClaimReward
| Type | Attribute Key | Attribute Value |
|----------------------|---------------------|--------------------|
| claim_reward | claimed_by | {claiming address} |
| claim_reward | claim_amount | {amount claimed} |
| message | module | incentive |
| message | sender | {sender address} |
## BeginBlock
| Type | Attribute Key | Attribute Value |
|----------------------|---------------------|--------------------|
| new_claim_period | claim_period | {claim period} |
| new_reward_period | reward_period | {reward period} |
| claim_period_expiry | claim_period | {claim period} |

View File

@ -0,0 +1,19 @@
# Parameters
The incentive module contains the following parameters:
| Key | Type | Example | Description |
|------------|----------------|---------------|--------------------------------------------------|
| Active | bool | "true" | boolean for if this module is active |
| Rewards | array (Reward) | [{see below}] | array of params for each inflationary period |
Each `Reward` has the following parameters
| Key | Type | Example | Description |
|------------------|--------------------|----------------------------------|----------------------------------------------------------------|
| Active | bool | "true | boolean for if rewards for this collateral are active |
| Denom | string | "bnb" | the collateral for which rewards are eligible |
| AvailableRewards | object (coin) | {"denom":"kava","amount":"1000"} | the rewards available per reward period |
| Duration | string (time ns) | "172800000000000" | the duration of each reward period |
| TimeLock | string (time ns) | "172800000000000" | the duration for which claimed rewards will be vesting |
| ClaimDuration | string (time ns) | "172800000000000" | how long users have to claim rewards before they expire |

View File

@ -0,0 +1,11 @@
# Begin Block
At the start of each block, expired claims and claim periods are deleted, rewards are applied to CDPs for any ongoing reward periods, expired reward periods are deleted and replaced with a new reward period (if active), and claim periods are created for expiring reward periods. The logic is as follows:
```go
func BeginBlocker(ctx sdk.Context, k Keeper) {
k.DeleteExpiredClaimsAndClaimPeriods(ctx)
k.ApplyRewardsToCdps(ctx)
k.CreateAndDeleteRewardPeriods(ctx)
}
```

View File

@ -0,0 +1,17 @@
# `incentive`
<!-- TOC -->
1. **[Concepts](01_concepts.md)**
2. **[State](02_state.md)**
3. **[Messages](03_messages.md)**
4. **[Events](04_events.md)**
5. **[Params](05_params.md)**
6. **[BeginBlock](06_begin_block.md)**
## Abstract
`x/incentive` is an implementation of a Cosmos SDK Module that allows for governance controlled user incentives for users who create stable assets by opening a collateralized debt position (CDP). Governance proposes an array of collateral rewards, with each item representing a collateral type that will be eligible for rewards. Each collateral reward specifies the number of coins awarded per period, the length of rewards periods, the length of claim periods. Governance can alter the collateral rewards using parameter change proposals as well as adding or removing collateral types. All changes to parameters would take place in the _next_ period.
### Dependencies
This module depends on `x/cdp` for users to be able to create CDPs and on `x/kavadist`, which controls the module account from where rewards are spent. In the event that the module account is not funded, user's attempt to claim rewards will fail.