mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10:05:18 +00:00
x: add spec for remaining modules (#494)
[R4R] Add spec for remaining modules
This commit is contained in:
commit
5e931863fd
@ -22,7 +22,7 @@ const (
|
||||
EventTypeCdpLiquidation = types.EventTypeCdpLiquidation
|
||||
EventTypeBeginBlockerFatal = types.EventTypeBeginBlockerFatal
|
||||
AttributeKeyCdpID = types.AttributeKeyCdpID
|
||||
AttributeKeyDepositor = types.AttributeKeyDepositor
|
||||
AttributeKeyDeposit = types.AttributeKeyDeposit
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
AttributeKeyError = types.AttributeKeyError
|
||||
ModuleName = types.ModuleName
|
||||
|
@ -40,7 +40,7 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error {
|
||||
types.EventTypeCdpLiquidation,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(types.AttributeKeyCdpID, fmt.Sprintf("%d", cdp.ID)),
|
||||
sdk.NewAttribute(types.AttributeKeyDepositor, fmt.Sprintf("%s", dep.Depositor)),
|
||||
sdk.NewAttribute(types.AttributeKeyDeposit, dep.String()),
|
||||
),
|
||||
)
|
||||
err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, types.LiquidatorMacc, sdk.NewCoins(dep.Amount))
|
||||
|
@ -18,10 +18,12 @@ The cdp module emits the following events:
|
||||
|
||||
### MsgWithdraw
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|---------|---------------|------------------|
|
||||
| message | module | cdp |
|
||||
| message | sender | {sender address} |
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|---------|--------------- |-----------------------|
|
||||
| message | cdp_withdrawal | {collateral amount} |
|
||||
| message | cdp_id | {cdp_id} |
|
||||
| message | module | cdp |
|
||||
| message | sender | {sender address} |
|
||||
|
||||
### MsgDeposit
|
||||
|
||||
@ -43,10 +45,13 @@ The cdp module emits the following events:
|
||||
|
||||
### MsgRepayDebt
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|---------|---------------|------------------|
|
||||
| message | module | cdp |
|
||||
| message | sender | {sender address} |
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|---------------|---------------|--------------------|
|
||||
| cdp_repayment | amount | {repayment amount} |
|
||||
| cdp_repayment | cdp_id | {cdp id} |
|
||||
| cdp_close | cdp_id | {cdp id} |
|
||||
| message | module | cdp |
|
||||
| message | sender | {sender address} |
|
||||
|
||||
## BeginBlock
|
||||
|
||||
@ -54,6 +59,6 @@ The cdp module emits the following events:
|
||||
|-------------------------|---------------|---------------------|
|
||||
| cdp_liquidation | module | cdp |
|
||||
| cdp_liquidation | cdp_id | {cdp id} |
|
||||
| cdp_liquidation | depositor | {depositor address} |
|
||||
| cdp_liquidation | deposit | {deposit} |
|
||||
| cdp_begin_blocker_error | module | cdp |
|
||||
| cdp_begin_blocker_error | error_message | {error} |
|
||||
|
@ -12,7 +12,7 @@ const (
|
||||
EventTypeBeginBlockerFatal = "cdp_begin_block_error"
|
||||
|
||||
AttributeKeyCdpID = "cdp_id"
|
||||
AttributeKeyDepositor = "depositor"
|
||||
AttributeKeyDeposit = "deposit"
|
||||
AttributeValueCategory = "cdp"
|
||||
AttributeKeyError = "error_message"
|
||||
)
|
||||
|
@ -12,6 +12,7 @@ const (
|
||||
AttributeKeyCommitteeID = types.AttributeKeyCommitteeID
|
||||
AttributeKeyProposalCloseStatus = types.AttributeKeyProposalCloseStatus
|
||||
AttributeKeyProposalID = types.AttributeKeyProposalID
|
||||
AttributeKeyVoter = types.AttributeKeyVoter
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
AttributeValueProposalFailed = types.AttributeValueProposalFailed
|
||||
AttributeValueProposalPassed = types.AttributeValueProposalPassed
|
||||
|
@ -74,6 +74,7 @@ func (k Keeper) AddVote(ctx sdk.Context, proposalID uint64, voter sdk.AccAddress
|
||||
types.EventTypeProposalVote,
|
||||
sdk.NewAttribute(types.AttributeKeyCommitteeID, fmt.Sprintf("%d", com.ID)),
|
||||
sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", pr.ID)),
|
||||
sdk.NewAttribute(types.AttributeKeyVoter, voter.String()),
|
||||
),
|
||||
)
|
||||
return nil
|
||||
|
5
x/committee/spec/01_concepts.md
Normal file
5
x/committee/spec/01_concepts.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Concepts
|
||||
|
||||
For a general introduction to governance using the Comsos-SDK, see [x/gov](https://github.com/cosmos/cosmos-sdk/blob/v0.38.3/x/gov/spec/01_concepts.md).
|
||||
|
||||
This module provides companion governance functionality to `x/gov` by allowing the creation of committees, or groups of addresses that can vote on proposals for which they have permission and which bypass the usual on-chain governance structures. Permissions scope the types of proposals that committees can submit and vote on. This allows for committees with unlimited breadth (ie, a committee can have permission to perform any governance action), or narrowly scoped abilities (ie, a committee can only change a single parameter of a single module within a specified range). Further, vote tallying is "first-past-the-post", so proposals can be enacted more rapidly and with greater flexibility than permitted by `x/gov`.
|
19
x/committee/spec/02_state.md
Normal file
19
x/committee/spec/02_state.md
Normal file
@ -0,0 +1,19 @@
|
||||
# State
|
||||
|
||||
## Genesis state
|
||||
|
||||
`GenesisState` defines the state that must be persisted when the blockchain stops/restarts in order for normal function of the committee module to resume.
|
||||
|
||||
```go
|
||||
// GenesisState is state that must be provided at chain genesis.
|
||||
type GenesisState struct {
|
||||
NextProposalID uint64 `json:"next_proposal_id" yaml:"next_proposal_id"`
|
||||
Committees []Committee `json:"committees" yaml:"committees"`
|
||||
Proposals []Proposal `json:"proposals" yaml:"proposals"`
|
||||
Votes []Vote `json:"votes" yaml:"votes"`
|
||||
}
|
||||
```
|
||||
|
||||
## Store
|
||||
|
||||
For complete implementation details for how items are stored, see [keys.go](../types/keys.go). The committee module store state consists of committees, proposals, and votes. When a proposal expires or passes, the proposal and associated votes are deleted from state.
|
34
x/committee/spec/03_messages.md
Normal file
34
x/committee/spec/03_messages.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Messages
|
||||
|
||||
Committee members submit proposals using a `MsgSubmitProposal`
|
||||
|
||||
```go
|
||||
// MsgSubmitProposal is used by committee members to create a new proposal that they can vote on.
|
||||
type MsgSubmitProposal struct {
|
||||
PubProposal PubProposal `json:"pub_proposal" yaml:"pub_proposal"`
|
||||
Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"`
|
||||
CommitteeID uint64 `json:"committee_id" yaml:"committee_id"`
|
||||
}
|
||||
```
|
||||
|
||||
## State Modifications
|
||||
|
||||
* Generate new `ProposalID`
|
||||
* Create new `Proposal` with deadline equal to the time that the proposal will expire.
|
||||
|
||||
Committee members vote 'yes' on a proposal using a `MsgVote`
|
||||
|
||||
```go
|
||||
// MsgVote is submitted by committee members to vote on proposals.
|
||||
type MsgVote struct {
|
||||
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"`
|
||||
Voter sdk.AccAddress `json:"voter" yaml:"voter"`
|
||||
}
|
||||
```
|
||||
|
||||
## State Modifications
|
||||
|
||||
* Create a new `Vote`
|
||||
* If the proposal is over the threshold:
|
||||
* Enact the proposal (proposals may cause state modifications)
|
||||
* Delete the proposal and associated votes
|
33
x/committee/spec/04_events.md
Normal file
33
x/committee/spec/04_events.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Events
|
||||
|
||||
The `x/committee` module emits the following events:
|
||||
|
||||
## MsgSubmitProposal
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|----------------------|---------------------|--------------------|
|
||||
| proposal_submit | committee_id | {committee ID} |
|
||||
| proposal_submit | proposal_id | {proposal ID} |
|
||||
| message | module | committee |
|
||||
| message | sender | {sender address} |
|
||||
|
||||
## MsgVote
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|----------------------|---------------------|--------------------|
|
||||
| proposal_vote | committee_id | {committee ID} |
|
||||
| proposal_vote | proposal_id | {proposal ID} |
|
||||
| proposal_vote | voter | {voter address} |
|
||||
| proposal_close | committee_id | {committee ID} |
|
||||
| proposal_close | proposal_id | {proposal ID} |
|
||||
| proposal_close | status | {outcome} |
|
||||
| message | module | committee |
|
||||
| message | sender | {sender address} |
|
||||
|
||||
## BeginBlock
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|----------------------|---------------------|--------------------|
|
||||
| proposal_close | committee_id | {committee ID} |
|
||||
| proposal_close | proposal_id | {proposal ID} |
|
||||
| proposal_close | status | proposal_timeout |
|
3
x/committee/spec/05_params.md
Normal file
3
x/committee/spec/05_params.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Parameters
|
||||
|
||||
The committee module has no parameters. Committees are created using the `x/gov` module and and inherit the parameters controlling governance proposals from `x/gov`.
|
10
x/committee/spec/06_begin_block.md
Normal file
10
x/committee/spec/06_begin_block.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Begin Block
|
||||
|
||||
At the start of each block, expired proposals are deleted. The logic is as follows:
|
||||
|
||||
```go
|
||||
// BeginBlocker runs at the start of every block.
|
||||
func BeginBlocker(ctx sdk.Context, _ abci.RequestBeginBlock, k Keeper) {
|
||||
k.CloseExpiredProposals(ctx)
|
||||
}
|
||||
```
|
@ -3,17 +3,25 @@
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!-- 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)**
|
||||
|
||||
## Overview
|
||||
|
||||
The `x/committee` module is an additional governance module to `cosmos-sdk/x/gov`.
|
||||
The `x/committee` module is Cosmos-SDK module that acts as an additional governance module to `cosmos-sdk/x/gov`.
|
||||
|
||||
It allows groups of accounts to vote on and enact proposals without a full chain governance vote. Certain proposal types can then be decided on quickly in emergency situations, or low risk parameter updates can be delegated to a smaller group of individuals.
|
||||
|
||||
Committees work with "proposals", using the same type from the `gov` module so they are compatible with all existing proposal types such as param changes, or community pool spend, or text proposals.
|
||||
|
||||
Committees have members and permissions.
|
||||
Committees have members and permissions. Committees are 'elected' via traditional `gov` proposals - ie. all coin-holders vote on the creation, deletion, and updating of committees.
|
||||
|
||||
Members vote on proposals, with just simple one vote per member, no deposits or slashing. More sophisticated voting could be added.
|
||||
Members of committees vote on proposals, with one vote per member and no deposits or slashing. Only a member of a committee can submit a proposal for that committee. More sophisticated voting could be added, as well as the ability for committees to edit themselves or other committees. A proposal passes when the number of votes is over the threshold for that committee. Vote thresholds are set per committee. Committee members vote yes by casting a vote and vote no by abstaining from voting and letting the proposal expire.
|
||||
|
||||
Permissions scope the allowed set of proposals a committee can enact. For example:
|
||||
|
||||
@ -21,6 +29,4 @@ Permissions scope the allowed set of proposals a committee can enact. For exampl
|
||||
- allow the committee to change auction bid increments, but only within the range [0, 0.1]
|
||||
- allow the committee to only disable cdp msg types, but not staking or gov
|
||||
|
||||
A permission acts as a filter for incoming gov proposals, rejecting them if they do not pass. A permission can be any type with a method `Allows(p Proposal) bool`. They reject all proposals that they don't explicitly allow.
|
||||
|
||||
This allows permissions to be parameterized to allow fine grained control specified at runtime. For example a generic parameter permission type can allow a committee to only change a particular param, or only change params within a certain percentage.
|
||||
A permission acts as a filter for incoming gov proposals, rejecting them at the handler if they do not have the required permissions. A permission can be any type with a method `Allows(p Proposal) bool`. The handler will reject all proposals that are not explicitly allowed. This allows permissions to be parameterized to allow fine grained control specified at runtime. For example a generic parameter permission type can allow a committee to only change a particular param, or only change params within a certain range.
|
||||
|
@ -10,6 +10,7 @@ const (
|
||||
AttributeKeyCommitteeID = "committee_id"
|
||||
AttributeKeyProposalID = "proposal_id"
|
||||
AttributeKeyProposalCloseStatus = "status"
|
||||
AttributeKeyVoter = "voter"
|
||||
AttributeValueProposalPassed = "proposal_passed"
|
||||
AttributeValueProposalTimeout = "proposal_timeout"
|
||||
AttributeValueProposalFailed = "proposal_failed"
|
||||
|
@ -1,9 +1,10 @@
|
||||
package incentive
|
||||
|
||||
// nolint
|
||||
// autogenerated code using github.com/rigelrozanski/multitool
|
||||
// aliases generated for the following subdirectories:
|
||||
// ALIASGEN: github.com/kava-labs/kava/x/incentive/keeper
|
||||
// ALIASGEN: github.com/kava-labs/kava/x/incentive/types
|
||||
package incentive
|
||||
|
||||
import (
|
||||
"github.com/kava-labs/kava/x/incentive/keeper"
|
||||
@ -11,18 +12,24 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
EventTypeClaim = types.EventTypeClaim
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
AttributeKeySender = types.AttributeKeySender
|
||||
ModuleName = types.ModuleName
|
||||
StoreKey = types.StoreKey
|
||||
RouterKey = types.RouterKey
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
QuerierRoute = types.QuerierRoute
|
||||
QueryGetClaims = types.QueryGetClaims
|
||||
RestClaimOwner = types.RestClaimOwner
|
||||
RestClaimDenom = types.RestClaimDenom
|
||||
QueryGetParams = types.QueryGetParams
|
||||
EventTypeClaim = types.EventTypeClaim
|
||||
EventTypeRewardPeriod = types.EventTypeRewardPeriod
|
||||
EventTypeClaimPeriod = types.EventTypeClaimPeriod
|
||||
EventTypeClaimPeriodExpiry = types.EventTypeClaimPeriodExpiry
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
AttributeKeyClaimedBy = types.AttributeKeyClaimedBy
|
||||
AttributeKeyClaimAmount = types.AttributeKeyClaimAmount
|
||||
AttributeKeyRewardPeriod = types.AttributeKeyRewardPeriod
|
||||
AttributeKeyClaimPeriod = types.AttributeKeyClaimPeriod
|
||||
ModuleName = types.ModuleName
|
||||
StoreKey = types.StoreKey
|
||||
RouterKey = types.RouterKey
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
QuerierRoute = types.QuerierRoute
|
||||
QueryGetClaims = types.QueryGetClaims
|
||||
RestClaimOwner = types.RestClaimOwner
|
||||
RestClaimDenom = types.RestClaimDenom
|
||||
QueryGetParams = types.QueryGetParams
|
||||
)
|
||||
|
||||
var (
|
||||
@ -46,6 +53,7 @@ var (
|
||||
NewRewardPeriod = types.NewRewardPeriod
|
||||
NewClaimPeriod = types.NewClaimPeriod
|
||||
NewClaim = types.NewClaim
|
||||
NewRewardPeriodFromReward = types.NewRewardPeriodFromReward
|
||||
|
||||
// variable aliases
|
||||
ModuleCdc = types.ModuleCdc
|
||||
|
@ -1,8 +1,6 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
@ -29,10 +27,13 @@ func (k Keeper) PayoutClaim(ctx sdk.Context, addr sdk.AccAddress, denom string,
|
||||
return err
|
||||
}
|
||||
|
||||
k.DeleteClaim(ctx, addr, denom, id)
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeClaim,
|
||||
sdk.NewAttribute(types.AttributeKeySender, fmt.Sprintf("%s", addr)),
|
||||
sdk.NewAttribute(types.AttributeKeyClaimedBy, addr.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.Reward.String()),
|
||||
),
|
||||
)
|
||||
return nil
|
||||
@ -103,6 +104,13 @@ func (k Keeper) DeleteExpiredClaimsAndClaimPeriods(ctx sdk.Context) {
|
||||
return false
|
||||
})
|
||||
k.DeleteClaimPeriod(ctx, cp.ID, cp.Denom)
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeClaimPeriodExpiry,
|
||||
sdk.NewAttribute(types.AttributeKeyClaimPeriod, cp.String()),
|
||||
),
|
||||
)
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
@ -20,18 +20,15 @@ func (k Keeper) HandleRewardPeriodExpiry(ctx sdk.Context, rp types.RewardPeriod)
|
||||
|
||||
// CreateNewRewardPeriod creates a new reward period from the input reward
|
||||
func (k Keeper) CreateNewRewardPeriod(ctx sdk.Context, reward types.Reward) {
|
||||
// reward periods store the amount of rewards paid PER SECOND
|
||||
rewardsPerSecond := sdk.NewDecFromInt(reward.AvailableRewards.Amount).Quo(sdk.NewDecFromInt(sdk.NewInt(int64(reward.Duration.Seconds())))).TruncateInt()
|
||||
rewardCoinPerSecond := sdk.NewCoin(reward.AvailableRewards.Denom, rewardsPerSecond)
|
||||
rp := types.RewardPeriod{
|
||||
Denom: reward.Denom,
|
||||
Start: ctx.BlockTime(),
|
||||
End: ctx.BlockTime().Add(reward.Duration),
|
||||
Reward: rewardCoinPerSecond,
|
||||
ClaimEnd: ctx.BlockTime().Add(reward.Duration).Add(reward.ClaimDuration),
|
||||
ClaimTimeLock: reward.TimeLock,
|
||||
}
|
||||
rp := types.NewRewardPeriodFromReward(reward, ctx.BlockTime())
|
||||
k.SetRewardPeriod(ctx, rp)
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeRewardPeriod,
|
||||
sdk.NewAttribute(types.AttributeKeyRewardPeriod, rp.String()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// CreateAndDeleteRewardPeriods creates reward periods for active rewards that don't already have a reward period and deletes reward periods for inactive rewards that currently have a reward period
|
||||
@ -95,6 +92,12 @@ func (k Keeper) ApplyRewardsToCdps(ctx sdk.Context) {
|
||||
func (k Keeper) CreateUniqueClaimPeriod(ctx sdk.Context, denom string, end time.Time, timeLock time.Duration) {
|
||||
id := k.GetNextClaimPeriodID(ctx, denom)
|
||||
claimPeriod := types.NewClaimPeriod(denom, id, end, timeLock)
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeClaimPeriod,
|
||||
sdk.NewAttribute(types.AttributeKeyClaimPeriod, claimPeriod.String()),
|
||||
),
|
||||
)
|
||||
k.SetClaimPeriod(ctx, claimPeriod)
|
||||
k.SetNextClaimPeriodID(ctx, denom, id+1)
|
||||
}
|
||||
|
5
x/incentive/spec/01_concepts.md
Normal file
5
x/incentive/spec/01_concepts.md
Normal 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.
|
57
x/incentive/spec/02_state.md
Normal file
57
x/incentive/spec/02_state.md
Normal 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 `Claim` is created in the store for all CDP owners, if one doesn't already exist. The claim object is associated with a `ClaimPeriod` via the ID. This implies that a `Claim` is 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 `Claim` objects, and 2) 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 `Claim` 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 `Claim` 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.
|
16
x/incentive/spec/03_messages.md
Normal file
16
x/incentive/spec/03_messages.md
Normal 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
|
20
x/incentive/spec/04_events.md
Normal file
20
x/incentive/spec/04_events.md
Normal 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} |
|
19
x/incentive/spec/05_params.md
Normal file
19
x/incentive/spec/05_params.md
Normal 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 |
|
11
x/incentive/spec/06_begin_block.md
Normal file
11
x/incentive/spec/06_begin_block.md
Normal 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)
|
||||
}
|
||||
```
|
17
x/incentive/spec/README.md
Normal file
17
x/incentive/spec/README.md
Normal 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.
|
@ -1,8 +1,15 @@
|
||||
package types
|
||||
|
||||
// Events emitted by the incentive module
|
||||
const (
|
||||
EventTypeClaim = "claim_reward"
|
||||
EventTypeClaim = "claim_reward"
|
||||
EventTypeRewardPeriod = "new_reward_period"
|
||||
EventTypeClaimPeriod = "new_claim_period"
|
||||
EventTypeClaimPeriodExpiry = "claim_period_expiry"
|
||||
|
||||
AttributeValueCategory = ModuleName
|
||||
AttributeKeySender = "sender"
|
||||
AttributeValueCategory = ModuleName
|
||||
AttributeKeyClaimedBy = "claimed_by"
|
||||
AttributeKeyClaimAmount = "claim_amount"
|
||||
AttributeKeyRewardPeriod = "reward_period"
|
||||
AttributeKeyClaimPeriod = "claim_period"
|
||||
)
|
||||
|
@ -25,8 +25,8 @@ func (rp RewardPeriod) String() string {
|
||||
End: %s,
|
||||
Reward: %s,
|
||||
Claim End: %s,
|
||||
Claim Time Lock: %s`,
|
||||
rp.Denom, rp.Start, rp.End, rp.Reward, rp.ClaimEnd, rp.ClaimTimeLock)
|
||||
Claim Time Lock: %s
|
||||
`, rp.Denom, rp.Start, rp.End, rp.Reward, rp.ClaimEnd, rp.ClaimTimeLock)
|
||||
}
|
||||
|
||||
// NewRewardPeriod returns a new RewardPeriod
|
||||
@ -52,6 +52,16 @@ type ClaimPeriod struct {
|
||||
TimeLock time.Duration `json:"time_lock" yaml:"time_lock"`
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (cp ClaimPeriod) String() string {
|
||||
return fmt.Sprintf(`Claim Period:
|
||||
Denom: %s,
|
||||
ID: %d,
|
||||
End: %s,
|
||||
Claim Time Lock: %s
|
||||
`, cp.Denom, cp.ID, cp.End, cp.TimeLock)
|
||||
}
|
||||
|
||||
// NewClaimPeriod returns a new ClaimPeriod
|
||||
func NewClaimPeriod(denom string, id uint64, end time.Time, timeLock time.Duration) ClaimPeriod {
|
||||
return ClaimPeriod{
|
||||
@ -89,9 +99,24 @@ func (c Claim) String() string {
|
||||
Owner: %s,
|
||||
Denom: %s,
|
||||
Reward: %s,
|
||||
Claim Period ID: %d,`,
|
||||
c.Owner, c.Denom, c.Reward, c.ClaimPeriodID)
|
||||
Claim Period ID: %d,
|
||||
`, c.Owner, c.Denom, c.Reward, c.ClaimPeriodID)
|
||||
}
|
||||
|
||||
// Claims array of Claim
|
||||
type Claims []Claim
|
||||
|
||||
// NewRewardPeriodFromReward returns a new reward period from the input reward and block time
|
||||
func NewRewardPeriodFromReward(reward Reward, blockTime time.Time) RewardPeriod {
|
||||
// note: reward periods store the amount of rewards paid PER SECOND
|
||||
rewardsPerSecond := sdk.NewDecFromInt(reward.AvailableRewards.Amount).Quo(sdk.NewDecFromInt(sdk.NewInt(int64(reward.Duration.Seconds())))).TruncateInt()
|
||||
rewardCoinPerSecond := sdk.NewCoin(reward.AvailableRewards.Denom, rewardsPerSecond)
|
||||
return RewardPeriod{
|
||||
Denom: reward.Denom,
|
||||
Start: blockTime,
|
||||
End: blockTime.Add(reward.Duration),
|
||||
Reward: rewardCoinPerSecond,
|
||||
ClaimEnd: blockTime.Add(reward.Duration).Add(reward.ClaimDuration),
|
||||
ClaimTimeLock: reward.TimeLock,
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ func (k Keeper) mintInflationaryCoins(ctx sdk.Context, inflationRate sdk.Dec, ti
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeKavaDist,
|
||||
sdk.NewAttribute(types.AttributeKeyInflation, amountToMint.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyInflation, sdk.NewCoin(denom, amountToMint).String()),
|
||||
),
|
||||
)
|
||||
return nil
|
||||
|
3
x/kavadist/spec/01_concepts.md
Normal file
3
x/kavadist/spec/01_concepts.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Concepts
|
||||
|
||||
The minting mechanism in this module is designed to allow governance to determine a set of inflationary periods and the APR rate of inflation for each period. This module mints coins each block according to the schedule such that after 1 year the APR inflation worth of coins will have been minted. Governance can alter the APR inflation using a parameter change proposal. Parameter change proposals that change the APR will take effect in the block after they pass.
|
30
x/kavadist/spec/02_state.md
Normal file
30
x/kavadist/spec/02_state.md
Normal file
@ -0,0 +1,30 @@
|
||||
# State
|
||||
|
||||
## Parameters and Genesis State
|
||||
|
||||
`Parameters` define the rate at which inflationary coins are minted and for how long inflationary periods last.
|
||||
|
||||
```go
|
||||
// Params governance parameters for kavadist module
|
||||
type Params struct {
|
||||
Active bool `json:"active" yaml:"active"`
|
||||
Periods Periods `json:"periods" yaml:"periods"`
|
||||
}
|
||||
|
||||
// Period stores the specified start and end dates, and the inflation, expressed as a decimal representing the yearly APR of tokens that will be minted during that period
|
||||
type Period struct {
|
||||
Start time.Time `json:"start" yaml:"start"` // example "2020-03-01T15:20:00Z"
|
||||
End time.Time `json:"end" yaml:"end"` // example "2020-06-01T15:20:00Z"
|
||||
Inflation sdk.Dec `json:"inflation" yaml:"inflation"` // example "1.000000003022265980" - 10% inflation
|
||||
}
|
||||
```
|
||||
|
||||
`GenesisState` defines the state that must be persisted when the blockchain stops/restarts in order for normal function of the kavadist 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"`
|
||||
}
|
||||
```
|
3
x/kavadist/spec/03_messages.md
Normal file
3
x/kavadist/spec/03_messages.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Messages
|
||||
|
||||
There are no messages in the kavadist module. All state transitions are controlled by parameters, which can be updated via parameter change proposals.
|
10
x/kavadist/spec/04_events.md
Normal file
10
x/kavadist/spec/04_events.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Events
|
||||
|
||||
The `x/kavadist` module emits the following events:
|
||||
|
||||
## BeginBlock
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|----------------------|---------------------|-----------------|
|
||||
| kavadist | kava_dist_inflation | {amount} |
|
||||
| kavadist | kava_dist_status | "inactive" |
|
15
x/kavadist/spec/05_params.md
Normal file
15
x/kavadist/spec/05_params.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Parameters
|
||||
|
||||
The kavadist module has the following parameters:
|
||||
|
||||
| Key | Type | Example | Description |
|
||||
|------------|----------------|---------------|--------------------------------------------------|
|
||||
| Periods | array (Period) | [{see below}] | array of params for each inflationary period |
|
||||
|
||||
Each `Period` has the following parameters
|
||||
|
||||
| Key | Type | Example | Description |
|
||||
|------------|--------------------|--------------------------|----------------------------------------------------------------|
|
||||
| Start | time.Time | "2020-03-01T15:20:00Z" | the time when the period will start |
|
||||
| End | time.Time | "2020-06-01T15:20:00Z" | the time when the period will end |
|
||||
| Inflation | sdk.Dec | "1.000000003022265980" | the per-second inflation for the period |
|
12
x/kavadist/spec/06_begin_block.md
Normal file
12
x/kavadist/spec/06_begin_block.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Begin Block
|
||||
|
||||
At the start of each block, the inflationary coins for the ongoing period, if any, are minted. The logic is as follows:
|
||||
|
||||
```go
|
||||
func BeginBlocker(ctx sdk.Context, k Keeper) {
|
||||
err := k.MintPeriodInflation(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
13
x/kavadist/spec/README.md
Normal file
13
x/kavadist/spec/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# `kavadist`
|
||||
|
||||
<!-- 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/kavadist` is an implementation of a Cosmos SDK Module that allows for governance controlled minting of coins into a module account. Coins are minted during inflationary periods, which each period have a governance specified APR and duration. This module does not provide functionality for spending or distributing the minted coins.
|
@ -9,15 +9,15 @@ import (
|
||||
// EndBlocker updates the current pricefeed
|
||||
func EndBlocker(ctx sdk.Context, k Keeper) {
|
||||
// Update the current price of each asset.
|
||||
for _, a := range k.GetMarkets(ctx) {
|
||||
if a.Active {
|
||||
err := k.SetCurrentPrices(ctx, a.MarketID)
|
||||
for _, market := range k.GetMarkets(ctx) {
|
||||
if market.Active {
|
||||
err := k.SetCurrentPrices(ctx, market.MarketID)
|
||||
if err != nil {
|
||||
// In the event of failure, emit an event.
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
EventTypeNoValidPrices,
|
||||
sdk.NewAttribute(AttributeKeyPriceUpdateFailed, fmt.Sprintf("%s", a.MarketID)),
|
||||
sdk.NewAttribute(AttributeMarketID, fmt.Sprintf("%s", market.MarketID)),
|
||||
),
|
||||
)
|
||||
continue
|
||||
|
@ -12,26 +12,25 @@ import (
|
||||
|
||||
// nolint
|
||||
const (
|
||||
EventTypeMarketPriceUpdated = types.EventTypeMarketPriceUpdated
|
||||
EventTypeOracleUpdatedPrice = types.EventTypeOracleUpdatedPrice
|
||||
EventTypeNoValidPrices = types.EventTypeNoValidPrices
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
AttributeMarketID = types.AttributeMarketID
|
||||
AttributeMarketPrice = types.AttributeMarketPrice
|
||||
AttributeOracle = types.AttributeOracle
|
||||
AttributeExpiry = types.AttributeExpiry
|
||||
AttributeKeyPriceUpdateFailed = types.AttributeKeyPriceUpdateFailed
|
||||
ModuleName = types.ModuleName
|
||||
StoreKey = types.StoreKey
|
||||
RouterKey = types.RouterKey
|
||||
QuerierRoute = types.QuerierRoute
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
TypeMsgPostPrice = types.TypeMsgPostPrice
|
||||
QueryGetParams = types.QueryGetParams
|
||||
QueryMarkets = types.QueryMarkets
|
||||
QueryOracles = types.QueryOracles
|
||||
QueryRawPrices = types.QueryRawPrices
|
||||
QueryPrice = types.QueryPrice
|
||||
EventTypeMarketPriceUpdated = types.EventTypeMarketPriceUpdated
|
||||
EventTypeOracleUpdatedPrice = types.EventTypeOracleUpdatedPrice
|
||||
EventTypeNoValidPrices = types.EventTypeNoValidPrices
|
||||
AttributeValueCategory = types.AttributeValueCategory
|
||||
AttributeMarketID = types.AttributeMarketID
|
||||
AttributeMarketPrice = types.AttributeMarketPrice
|
||||
AttributeOracle = types.AttributeOracle
|
||||
AttributeExpiry = types.AttributeExpiry
|
||||
ModuleName = types.ModuleName
|
||||
StoreKey = types.StoreKey
|
||||
RouterKey = types.RouterKey
|
||||
QuerierRoute = types.QuerierRoute
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
TypeMsgPostPrice = types.TypeMsgPostPrice
|
||||
QueryGetParams = types.QueryGetParams
|
||||
QueryMarkets = types.QueryMarkets
|
||||
QueryOracles = types.QueryOracles
|
||||
QueryRawPrices = types.QueryRawPrices
|
||||
QueryPrice = types.QueryPrice
|
||||
)
|
||||
|
||||
// nolint
|
||||
|
3
x/pricefeed/spec/01_concepts.md
Normal file
3
x/pricefeed/spec/01_concepts.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Concepts
|
||||
|
||||
Prices can be posted by any account which is added as an oracle. Oracles are specific to each market and can be updated via param change proposals. When an oracle posts a price, they submit a message to the blockchain that contains the current price for that market and a time when that price should be considered expired. If an oracle posts a new price, that price becomes the current price for that oracle, regardless of the previous price's expiry. A group of prices posted by a set of oracles for a particular market are referred to as 'raw prices' and the current median price of all valid oracle prices is referred to as the 'current price'. Each block, the current price for each market is determined by calculating the median of the raw prices.
|
44
x/pricefeed/spec/02_state.md
Normal file
44
x/pricefeed/spec/02_state.md
Normal file
@ -0,0 +1,44 @@
|
||||
# State
|
||||
|
||||
## Parameters and genesis state
|
||||
|
||||
`Paramaters` determine which markets are tracked by the pricefeed and which oracles are authorized to post prices for a given market. There is only one active parameter set at any given time. Updates to parameters can be made via on-chain parameter update proposals.
|
||||
|
||||
```go
|
||||
// Params params for pricefeed. Can be altered via governance
|
||||
type Params struct {
|
||||
Markets Markets `json:"markets" yaml:"markets"` // Array containing the markets supported by the pricefeed
|
||||
}
|
||||
|
||||
// Market an asset in the pricefeed
|
||||
type Market struct {
|
||||
MarketID string `json:"market_id" yaml:"market_id"`
|
||||
BaseAsset string `json:"base_asset" yaml:"base_asset"`
|
||||
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
|
||||
Oracles []sdk.AccAddress `json:"oracles" yaml:"oracles"`
|
||||
Active bool `json:"active" yaml:"active"`
|
||||
}
|
||||
|
||||
type Markets []Market
|
||||
```
|
||||
|
||||
`GenesisState` defines the state that must be persisted when the blockchain stops/stars in order for the normal function of the pricefeed to resume.
|
||||
|
||||
```go
|
||||
// GenesisState - pricefeed state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
Params Params `json:"params" yaml:"params"`
|
||||
PostedPrices []PostedPrice `json:"posted_prices" yaml:"posted_prices"`
|
||||
}
|
||||
|
||||
// PostedPrice price for market posted by a specific oracle
|
||||
type PostedPrice struct {
|
||||
MarketID string `json:"market_id" yaml:"market_id"`
|
||||
OracleAddress sdk.AccAddress `json:"oracle_address" yaml:"oracle_address"`
|
||||
Price sdk.Dec `json:"price" yaml:"price"`
|
||||
Expiry time.Time `json:"expiry" yaml:"expiry"`
|
||||
}
|
||||
|
||||
type PostedPrices []PostedPrice
|
||||
```
|
||||
|
20
x/pricefeed/spec/03_messages.md
Normal file
20
x/pricefeed/spec/03_messages.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Messages
|
||||
|
||||
## Posting Prices
|
||||
|
||||
An authorized oraclef for a particular market can post the current price for that market using the `MsgPostPrice` type.
|
||||
|
||||
```go
|
||||
// MsgPostPrice struct representing a posted price message.
|
||||
// Used by oracles to input prices to the pricefeed
|
||||
type MsgPostPrice struct {
|
||||
From sdk.AccAddress `json:"from" yaml:"from"` // client that sent in this address
|
||||
MarketID string `json:"market_id" yaml:"market_id"` // asset code used by exchanges/api
|
||||
Price sdk.Dec `json:"price" yaml:"price"` // price in decimal (max precision 18)
|
||||
Expiry time.Time `json:"expiry" yaml:"expiry"` // expiry time
|
||||
}
|
||||
```
|
||||
|
||||
### State Modifications
|
||||
|
||||
* Update the raw price for the oracle for this market. This replaces any previous price for that oracle.
|
22
x/pricefeed/spec/04_events.md
Normal file
22
x/pricefeed/spec/04_events.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Events
|
||||
|
||||
The `x/pricefeed` module emits the following events:
|
||||
|
||||
## MsgPostPrice
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|----------------------|---------------|------------------|
|
||||
| oracle_updated_price | market_id | {market ID} |
|
||||
| oracle_updated_price | oracle | {oracle} |
|
||||
| oracle_updated_price | market_price | {price} |
|
||||
| oracle_updated_price | expiry | {expiry} |
|
||||
| message | module | pricefeed |
|
||||
| message | sender | {sender address} |
|
||||
|
||||
## BeginBlock
|
||||
|
||||
| Type | Attribute Key | Attribute Value |
|
||||
|----------------------|-----------------|-----------------|
|
||||
| market_price_updated | market_id | {market ID} |
|
||||
| market_price_updated | market_price | {price} |
|
||||
| no_valid_prices | market_id | {market ID} |
|
17
x/pricefeed/spec/05_params.md
Normal file
17
x/pricefeed/spec/05_params.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Parameters
|
||||
|
||||
The pricefeed module has the following parameters:
|
||||
|
||||
| Key | Type | Example | Description |
|
||||
|------------|----------------|---------------|--------------------------------------------------|
|
||||
| Markets | array (Market) | [{see below}] | array of params for each market in the pricefeed |
|
||||
|
||||
Each `Market` has the following parameters
|
||||
|
||||
| Key | Type | Example | Description |
|
||||
|------------|--------------------|--------------------------|----------------------------------------------------------------|
|
||||
| MarketID | string | "bnb:usd" | identifier for the market -- **must** be unique across markets |
|
||||
| BaseAsset | string | "bnb" | the base asset for the market pair |
|
||||
| QuoteAsset | string | "usd" | the quote asset for the market pair |
|
||||
| Oracles | array (AccAddress) | ["kava1...", "kava1..."] | addresses which can post prices for the market |
|
||||
| Active | bool | true | flag to disable oracle interactions with the module |
|
26
x/pricefeed/spec/06_end_block.md
Normal file
26
x/pricefeed/spec/06_end_block.md
Normal file
@ -0,0 +1,26 @@
|
||||
# End Block
|
||||
|
||||
At the end of each block, the current price is calculated as the median of all raw prices for each market. The logic is as follows:
|
||||
|
||||
```go
|
||||
// EndBlocker updates the current pricefeed
|
||||
func EndBlocker(ctx sdk.Context, k Keeper) {
|
||||
// Update the current price of each asset.
|
||||
for _, market := range k.GetMarkets(ctx) {
|
||||
if market.Active {
|
||||
err := k.SetCurrentPrices(ctx, market.MarketID)
|
||||
if err != nil {
|
||||
// In the event of failure, emit an event.
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
EventTypeNoValidPrices,
|
||||
sdk.NewAttribute(AttributeMarketID, fmt.Sprintf("%s", market.MarketID)),
|
||||
),
|
||||
)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
13
x/pricefeed/spec/README.md
Normal file
13
x/pricefeed/spec/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# `pricefeed`
|
||||
|
||||
<!-- 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. **[EndBlock](06_end_block.md)**
|
||||
|
||||
## Abstract
|
||||
|
||||
`x/pricefeed` is an implementation of a Cosmos SDK Module that handles the posting of prices for various markets by a group of whitelisted oracles. At the end of each block, the median price of all oracle posted prices is determined for each market and stored.
|
@ -6,10 +6,9 @@ const (
|
||||
EventTypeOracleUpdatedPrice = "oracle_updated_price"
|
||||
EventTypeNoValidPrices = "no_valid_prices"
|
||||
|
||||
AttributeValueCategory = ModuleName
|
||||
AttributeMarketID = "market_id"
|
||||
AttributeMarketPrice = "market_price"
|
||||
AttributeOracle = "oracle"
|
||||
AttributeExpiry = "expiry"
|
||||
AttributeKeyPriceUpdateFailed = "price_update_failed"
|
||||
AttributeValueCategory = ModuleName
|
||||
AttributeMarketID = "market_id"
|
||||
AttributeMarketPrice = "market_price"
|
||||
AttributeOracle = "oracle"
|
||||
AttributeExpiry = "expiry"
|
||||
)
|
||||
|
@ -1,8 +1,7 @@
|
||||
# Concepts
|
||||
|
||||
The validator-vesting module is responsible for managing Validator Vesting Accounts, a vesting account for which the release of coins is tied to the validation of the blockchain. Validator Vesting Accounts implemnt the cosmos-sdk vesting account spec, which can be found [here](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec).
|
||||
The validator-vesting module is responsible for managing Validator Vesting Accounts, a vesting account for which the release of coins is tied to the validation of the blockchain. Validator Vesting Accounts implement the cosmos-sdk vesting account spec, which can be found [here](https://github.com/cosmos/cosmos-sdk/tree/master/x/auth/spec).
|
||||
|
||||
The main concept the Validator Vesting Account introduces is that of _conditional vesting_, or vesting accounts in which it is possible for some or all of the vesting coins to fail to vest. For Validator Vesting Accounts, vesting is broken down into user-specified __vesting periods__. Each vesting period specifies an amount of coins that could vest in that period, and how long the vesting period lasts.
|
||||
|
||||
For each vesting period, a __signing threshold__ is specified, which is the percentage of blocks that must be signed for the coins to successfully vest. After a period ends, coins that are successfully vested become freely spendable. Coins that do not successfuly vest are burned, or sent to an optional return address.
|
||||
|
||||
For each vesting period, a __signing threshold__ is specified, which is the percentage of blocks that must be signed for the coins to successfully vest. After a period ends, coins that are successfully vested become freely spendable. Coins that do not successfully vest are burned, or sent to an optional return address.
|
||||
|
@ -2,24 +2,26 @@
|
||||
|
||||
## Validator Vesting Account type
|
||||
|
||||
Validator Vesting Accounts implement the `cosmos-sdk` vesting account spec and extend the `PeriodicVestingAccountType`:
|
||||
Validator Vesting Accounts implement the `cosmos-sdk` vesting account interfaces and extends the `PeriodicVestingAccountType`:
|
||||
|
||||
```go
|
||||
type ValidatorVestingAccount struct {
|
||||
*vesting.PeriodicVestingAccount
|
||||
|
||||
ValidatorAddress sdk.ConsAddress // The validator address which will be used to check if blocks were signed
|
||||
ReturnAddress sdk.AccAddress `json:"return_address" yaml:"return_address"` // The account where coins will be returned in the event of a failed vesting period
|
||||
SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"` // The percentage of blocks, as an integer between 0 and 100, that must be signed each period for coins to successfully vest.
|
||||
MissingSignCount []int64 `json:"missing_sign_count" yaml:"missing_sign_count"` // An array of two integers which track the number of blocks that were not signed during the current period and the total number of blocks which have passed during the current period, respectively.
|
||||
VestingPeriodProgress [][]int `json:"vesting_period_progress" yaml:"vesting_period_progress"` //An 2d array with length equal to the number of vesting periods. After each period, the value at the first index of that period is updated with 1 to represent that the period is over. The value at the second index is updated to 0 for unsucessful vesting and 1 for successful vesting.
|
||||
DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"` // The debt currently owed by the account. Debt accumulates in the event of unsuccessful vesting periods.
|
||||
}
|
||||
VestingPeriodProgress [][]int `json:"vesting_period_progress" yaml:"vesting_period_progress"` //An 2d array with length equal to the number of vesting periods. After each period, the value at the first index of that period is updated with 1 to represent that the period is over. The value at the second index is updated to 0 for unsuccessful vesting and 1 for successful vesting.
|
||||
DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"` // The debt currently owed by the account. Debt accumulates in the event of unsuccessful vesting periods.
|
||||
}
|
||||
```
|
||||
|
||||
## Stores
|
||||
|
||||
There is one `KVStore` in `validator-vesting` which stores
|
||||
|
||||
* A mapping from each ValidatorVestingAccount `address` to `[]Byte{0}`
|
||||
* A mapping from `previous_block_time_prefix` to `time.Time`
|
||||
|
||||
The use of `[]Byte{0}` value for each `address` key reflects that this module only accesses the store to get or iterate over keys, and does not require storing an value.
|
||||
The use of `[]Byte{0}` value for each `address` key reflects that this module only accesses the store to get or iterate over keys, and does not require storing an value. The storage of the actual account state is done by the cosmos-sdk `x/auth` `AccountKeeper`.
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Begin Block
|
||||
|
||||
At each `BeginBlock`, all validator vesting accounts are iterated over to update the status of the current vesting period. Note that the address of each account is retreived by iterating over the keys in the `validator-vesting` store, while the account objects are stored and accessed using the `auth` module's `AccountKeeper`. For each account, the block count is incremented, the missed sign count is incremented if the validator did not sign the block or was not found in the validator set. By comparing the blocktime of the current `BeginBlock`, with the value of `previousBlockTime` stored in the `validator-vesting` store, it is determined if the end of the current period has been reached. If the current period has ended, the `VestingPeriodProgress` field is updated to reflect if the coins for the ending period successfully vested or not. After updates are made regarding the status of the current vesting period, any outstanding debt on the account is attempted to be collected. If there is enough `SpendableBalance` on the account to cover the debt, coins are sent to the `ReturnAdress` or burned. If there is not enough `SpendableBalance` to cover the debt, all delegations of the account are `Unbonded`. Once those unbonding events reach maturity, the coins freed from the undonding will be used to cover the debt. Finally, the time of the previous block is stored in the validator vesting account keeper, which is used to determine when a period has ended.
|
||||
At each `BeginBlock`, all validator vesting accounts are iterated over to update the status of the current vesting period. Note that the address of each account is retrieved by iterating over the keys in the `validator-vesting` store, while the account objects are stored and accessed using the `auth` module's `AccountKeeper`. For each account, the block count is incremented, the missed sign count is incremented if the validator did not sign the block or was not found in the validator set. By comparing the blocktime of the current `BeginBlock`, with the value of `previousBlockTime` stored in the `validator-vesting` store, it is determined if the end of the current period has been reached. If the current period has ended, the `VestingPeriodProgress` field is updated to reflect if the coins for the ending period successfully vested or not. After updates are made regarding the status of the current vesting period, any outstanding debt on the account is attempted to be collected. If there is enough `SpendableBalance` on the account to cover the debt, coins are sent to the `ReturnAdress` or burned. If there is not enough `SpendableBalance` to cover the debt, all delegations of the account are `Unbonded`. Once those unbonding events reach maturity, the coins freed from the unbonding will be used to cover the debt. Finally, the time of the previous block is stored in the validator vesting account keeper, which is used to determine when a period has ended.
|
||||
|
||||
```go
|
||||
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
|
||||
@ -42,4 +42,3 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
|
||||
k.SetPreviousBlockTime(ctx, currentBlockTime)
|
||||
}
|
||||
```
|
||||
|
||||
|
14
x/validator-vesting/spec/README.md
Normal file
14
x/validator-vesting/spec/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# `validator-vesting`
|
||||
|
||||
<!-- TOC -->
|
||||
1. **[Concepts](01_concepts.md)**
|
||||
2. **[State](02_state.md)**
|
||||
3. **[BeginBlock](03_begin_block.md)**
|
||||
|
||||
## Abstract
|
||||
|
||||
`x/validator-vesting` is an implementation of a Cosmos SDK sub-module that defines a new type of vesting account, `ValidatorVestingAccount`. This account implements the Cosmos SDK `VestingAccount` interface and extends it to add conditions to the vesting balance. In this implementation, in order to receive the vesting balance, the validator vesting account specifies a validator that must sign a given `SigningThreshold` of blocks during each vesting period in order for coins to successfully vest.
|
||||
|
||||
## Dependencies
|
||||
|
||||
This module uses the Cosmos SDK `x/auth` module and `x/auth/vesting` sub-module definitions of `Account` and `VestingAccount`. The actual state of a `ValidatorVestingAccount` is stored in the `x/auth` keeper, while this module merely stores a list of addresses that correspond to validator vesting accounts for fast iteration.
|
Loading…
Reference in New Issue
Block a user