mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 23:35:19 +00:00
Pricefeed fixes (#308)
* fix: remove redundant debt limit param * wip: test pricefeed genesis * fix: pricefeed querier * fix: comments, naming * fix: query path * fix: store methods * fix: query methods * fix: standardize genesis validation
This commit is contained in:
parent
dca59447aa
commit
d04aad5cc9
@ -21,7 +21,7 @@ func NewPricefeedGenState(asset string, price sdk.Dec) app.GenesisState {
|
|||||||
pfGenesis := pricefeed.GenesisState{
|
pfGenesis := pricefeed.GenesisState{
|
||||||
Params: pricefeed.Params{
|
Params: pricefeed.Params{
|
||||||
Markets: []pricefeed.Market{
|
Markets: []pricefeed.Market{
|
||||||
pricefeed.Market{MarketID: asset + ":usd", BaseAsset: asset, QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
pricefeed.Market{MarketID: asset + ":usd", BaseAsset: asset, QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PostedPrices: []pricefeed.PostedPrice{
|
PostedPrices: []pricefeed.PostedPrice{
|
||||||
@ -77,8 +77,8 @@ func NewPricefeedGenStateMulti() app.GenesisState {
|
|||||||
pfGenesis := pricefeed.GenesisState{
|
pfGenesis := pricefeed.GenesisState{
|
||||||
Params: pricefeed.Params{
|
Params: pricefeed.Params{
|
||||||
Markets: []pricefeed.Market{
|
Markets: []pricefeed.Market{
|
||||||
pricefeed.Market{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
pricefeed.Market{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
pricefeed.Market{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
pricefeed.Market{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PostedPrices: []pricefeed.PostedPrice{
|
PostedPrices: []pricefeed.PostedPrice{
|
||||||
|
@ -21,7 +21,7 @@ func NewPricefeedGenState(asset string, price sdk.Dec) app.GenesisState {
|
|||||||
pfGenesis := pricefeed.GenesisState{
|
pfGenesis := pricefeed.GenesisState{
|
||||||
Params: pricefeed.Params{
|
Params: pricefeed.Params{
|
||||||
Markets: []pricefeed.Market{
|
Markets: []pricefeed.Market{
|
||||||
pricefeed.Market{MarketID: asset + ":usd", BaseAsset: asset, QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
pricefeed.Market{MarketID: asset + ":usd", BaseAsset: asset, QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PostedPrices: []pricefeed.PostedPrice{
|
PostedPrices: []pricefeed.PostedPrice{
|
||||||
@ -77,8 +77,8 @@ func NewPricefeedGenStateMulti() app.GenesisState {
|
|||||||
pfGenesis := pricefeed.GenesisState{
|
pfGenesis := pricefeed.GenesisState{
|
||||||
Params: pricefeed.Params{
|
Params: pricefeed.Params{
|
||||||
Markets: []pricefeed.Market{
|
Markets: []pricefeed.Market{
|
||||||
pricefeed.Market{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
pricefeed.Market{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
pricefeed.Market{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: pricefeed.Oracles{}, Active: true},
|
pricefeed.Market{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PostedPrices: []pricefeed.PostedPrice{
|
PostedPrices: []pricefeed.PostedPrice{
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
// EndBlocker updates the current pricefeed
|
// EndBlocker updates the current pricefeed
|
||||||
func EndBlocker(ctx sdk.Context, k Keeper) {
|
func EndBlocker(ctx sdk.Context, k Keeper) {
|
||||||
// Update the current price of each asset.
|
// Update the current price of each asset.
|
||||||
for _, a := range k.GetMarketParams(ctx) {
|
for _, a := range k.GetMarkets(ctx) {
|
||||||
if a.Active {
|
if a.Active {
|
||||||
err := k.SetCurrentPrices(ctx, a.MarketID)
|
err := k.SetCurrentPrices(ctx, a.MarketID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -26,12 +26,12 @@ const (
|
|||||||
DefaultParamspace = types.DefaultParamspace
|
DefaultParamspace = types.DefaultParamspace
|
||||||
RawPriceFeedPrefix = types.RawPriceFeedPrefix
|
RawPriceFeedPrefix = types.RawPriceFeedPrefix
|
||||||
CurrentPricePrefix = types.CurrentPricePrefix
|
CurrentPricePrefix = types.CurrentPricePrefix
|
||||||
AssetPrefix = types.AssetPrefix
|
MarketPrefix = types.MarketPrefix
|
||||||
OraclePrefix = types.OraclePrefix
|
OraclePrefix = types.OraclePrefix
|
||||||
TypeMsgPostPrice = types.TypeMsgPostPrice
|
TypeMsgPostPrice = types.TypeMsgPostPrice
|
||||||
QueryCurrentPrice = types.QueryCurrentPrice
|
QueryCurrentPrice = types.QueryCurrentPrice
|
||||||
QueryRawPrices = types.QueryRawPrices
|
QueryRawPrices = types.QueryRawPrices
|
||||||
QueryAssets = types.QueryAssets
|
QueryMarkets = types.QueryMarkets
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -40,36 +40,32 @@ var (
|
|||||||
ErrEmptyInput = types.ErrEmptyInput
|
ErrEmptyInput = types.ErrEmptyInput
|
||||||
ErrExpired = types.ErrExpired
|
ErrExpired = types.ErrExpired
|
||||||
ErrNoValidPrice = types.ErrNoValidPrice
|
ErrNoValidPrice = types.ErrNoValidPrice
|
||||||
ErrInvalidAsset = types.ErrInvalidAsset
|
ErrInvalidMarket = types.ErrInvalidMarket
|
||||||
ErrInvalidOracle = types.ErrInvalidOracle
|
ErrInvalidOracle = types.ErrInvalidOracle
|
||||||
NewGenesisState = types.NewGenesisState
|
NewGenesisState = types.NewGenesisState
|
||||||
DefaultGenesisState = types.DefaultGenesisState
|
DefaultGenesisState = types.DefaultGenesisState
|
||||||
ValidateGenesis = types.ValidateGenesis
|
|
||||||
NewMsgPostPrice = types.NewMsgPostPrice
|
NewMsgPostPrice = types.NewMsgPostPrice
|
||||||
ParamKeyTable = types.ParamKeyTable
|
|
||||||
NewParams = types.NewParams
|
NewParams = types.NewParams
|
||||||
DefaultParams = types.DefaultParams
|
DefaultParams = types.DefaultParams
|
||||||
|
ParamKeyTable = types.ParamKeyTable
|
||||||
NewKeeper = keeper.NewKeeper
|
NewKeeper = keeper.NewKeeper
|
||||||
NewQuerier = keeper.NewQuerier
|
NewQuerier = keeper.NewQuerier
|
||||||
|
|
||||||
// variable aliases
|
// variable aliases
|
||||||
ModuleCdc = types.ModuleCdc
|
ModuleCdc = types.ModuleCdc
|
||||||
KeyMarkets = types.KeyMarkets
|
KeyMarkets = types.KeyMarkets
|
||||||
|
DefaultMarkets = types.DefaultMarkets
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
GenesisState = types.GenesisState
|
||||||
Market = types.Market
|
Market = types.Market
|
||||||
Markets = types.Markets
|
Markets = types.Markets
|
||||||
Oracle = types.Oracle
|
|
||||||
Oracles = types.Oracles
|
|
||||||
CurrentPrice = types.CurrentPrice
|
CurrentPrice = types.CurrentPrice
|
||||||
PostedPrice = types.PostedPrice
|
PostedPrice = types.PostedPrice
|
||||||
SortDecs = types.SortDecs
|
SortDecs = types.SortDecs
|
||||||
GenesisState = types.GenesisState
|
|
||||||
MsgPostPrice = types.MsgPostPrice
|
MsgPostPrice = types.MsgPostPrice
|
||||||
Params = types.Params
|
Params = types.Params
|
||||||
ParamSubspace = types.ParamSubspace
|
QueryPricesParams = types.QueryPricesParams
|
||||||
QueryRawPricesResp = types.QueryRawPricesResp
|
|
||||||
QueryAssetsResp = types.QueryAssetsResp
|
|
||||||
Keeper = keeper.Keeper
|
Keeper = keeper.Keeper
|
||||||
)
|
)
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetQueryCmd returns the cli query commands for this module
|
// GetQueryCmd returns the cli query commands for this module
|
||||||
func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
|
func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
// Group nameservice queries under a subcommand
|
// Group nameservice queries under a subcommand
|
||||||
pricefeedQueryCmd := &cobra.Command{
|
pricefeedQueryCmd := &cobra.Command{
|
||||||
Use: types.ModuleName,
|
Use: types.ModuleName,
|
||||||
@ -22,9 +22,9 @@ func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pricefeedQueryCmd.AddCommand(client.GetCommands(
|
pricefeedQueryCmd.AddCommand(client.GetCommands(
|
||||||
GetCmdCurrentPrice(storeKey, cdc),
|
GetCmdCurrentPrice(queryRoute, cdc),
|
||||||
GetCmdRawPrices(storeKey, cdc),
|
GetCmdRawPrices(queryRoute, cdc),
|
||||||
GetCmdAssets(storeKey, cdc),
|
GetCmdMarkets(queryRoute, cdc),
|
||||||
)...)
|
)...)
|
||||||
|
|
||||||
return pricefeedQueryCmd
|
return pricefeedQueryCmd
|
||||||
@ -33,21 +33,28 @@ func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
|
|||||||
// GetCmdCurrentPrice queries the current price of an asset
|
// GetCmdCurrentPrice queries the current price of an asset
|
||||||
func GetCmdCurrentPrice(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
func GetCmdCurrentPrice(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "price [assetCode]",
|
Use: "price [marketID]",
|
||||||
Short: "get the current price of an asset",
|
Short: "get the current price for the input market",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
assetCode := args[0]
|
marketID := args[0]
|
||||||
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/price/%s", queryRoute, assetCode), nil)
|
|
||||||
|
bz, err := cdc.MarshalJSON(types.QueryPricesParams{
|
||||||
|
MarketID: marketID,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error when querying current price - %s", err)
|
return err
|
||||||
fmt.Printf("could not get current price for - %s \n", assetCode)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
var out types.CurrentPrice
|
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryCurrentPrice)
|
||||||
cdc.MustUnmarshalJSON(res, &out)
|
|
||||||
return cliCtx.PrintOutput(out)
|
res, _, err := cliCtx.QueryWithData(route, bz)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var price types.CurrentPrice
|
||||||
|
cdc.MustUnmarshalJSON(res, &price)
|
||||||
|
return cliCtx.PrintOutput(price)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,39 +62,48 @@ func GetCmdCurrentPrice(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
|||||||
// GetCmdRawPrices queries the current price of an asset
|
// GetCmdRawPrices queries the current price of an asset
|
||||||
func GetCmdRawPrices(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
func GetCmdRawPrices(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "rawprices [assetCode]",
|
Use: "rawprices [marketID]",
|
||||||
Short: "get the raw oracle prices for an asset",
|
Short: "get the raw oracle prices for the input market",
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
assetCode := args[0]
|
marketID := args[0]
|
||||||
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/rawprices/%s", queryRoute, assetCode), nil)
|
|
||||||
|
bz, err := cdc.MarshalJSON(types.QueryPricesParams{
|
||||||
|
MarketID: marketID,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not get raw prices for - %s \n", assetCode)
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
var out types.QueryRawPricesResp
|
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryRawPrices)
|
||||||
cdc.MustUnmarshalJSON(res, &out)
|
|
||||||
return cliCtx.PrintOutput(out)
|
res, _, err := cliCtx.QueryWithData(route, bz)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var prices []types.PostedPrice
|
||||||
|
cdc.MustUnmarshalJSON(res, &prices)
|
||||||
|
return cliCtx.PrintOutput(prices)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCmdAssets queries list of assets in the pricefeed
|
// GetCmdMarkets queries list of markets in the pricefeed
|
||||||
func GetCmdAssets(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
func GetCmdMarkets(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "assets",
|
Use: "markets",
|
||||||
Short: "get the assets in the pricefeed",
|
Short: "get the markets in the pricefeed",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/assets", queryRoute), nil)
|
route := fmt.Sprintf("custom/%s/markets", queryRoute)
|
||||||
|
res, _, err := cliCtx.QueryWithData(route, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not get assets")
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
var out types.QueryAssetsResp
|
var markets types.Markets
|
||||||
cdc.MustUnmarshalJSON(res, &out)
|
cdc.MustUnmarshalJSON(res, &markets)
|
||||||
return cliCtx.PrintOutput(out)
|
return cliCtx.PrintOutput(markets)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,8 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command {
|
|||||||
// GetCmdPostPrice cli command for posting prices.
|
// GetCmdPostPrice cli command for posting prices.
|
||||||
func GetCmdPostPrice(cdc *codec.Codec) *cobra.Command {
|
func GetCmdPostPrice(cdc *codec.Codec) *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "postprice [assetCode] [price] [expiry]",
|
Use: "postprice [marketID] [price] [expiry]",
|
||||||
Short: "post the latest price for a particular asset",
|
Short: "post the latest price for a particular market",
|
||||||
Args: cobra.ExactArgs(3),
|
Args: cobra.ExactArgs(3),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
@ -51,8 +51,7 @@ func GetCmdPostPrice(cdc *codec.Codec) *cobra.Command {
|
|||||||
}
|
}
|
||||||
expiryInt, ok := sdk.NewIntFromString(args[2])
|
expiryInt, ok := sdk.NewIntFromString(args[2])
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Printf("invalid expiry - %s \n", args[2])
|
return fmt.Errorf("invalid expiry - %s", args[2])
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
expiry := tmtime.Canonical(time.Unix(expiryInt.Int64(), 0))
|
expiry := tmtime.Canonical(time.Unix(expiryInt.Int64(), 0))
|
||||||
|
|
||||||
|
57
x/pricefeed/client/rest/query.go
Normal file
57
x/pricefeed/client/rest/query.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/x/pricefeed/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// define routes that get registered by the main application
|
||||||
|
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||||
|
r.HandleFunc(fmt.Sprintf("/%s/rawprices/{%s}", types.ModuleName, restName), queryRawPricesHandler(cliCtx)).Methods("GET")
|
||||||
|
r.HandleFunc(fmt.Sprintf("/%s/currentprice/{%s}", types.ModuleName, restName), queryCurrentPriceHandler(cliCtx)).Methods("GET")
|
||||||
|
r.HandleFunc(fmt.Sprintf("/%s/markets", types.ModuleName), queryMarketsHandler(cliCtx)).Methods("GET")
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryRawPricesHandler(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
paramType := vars[restName]
|
||||||
|
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/rawprices/%s", paramType), nil)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rest.PostProcessResponse(w, cliCtx, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryCurrentPriceHandler(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
paramType := vars[restName]
|
||||||
|
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/price/%s", paramType), nil)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rest.PostProcessResponse(w, cliCtx, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryMarketsHandler(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/markets/"), nil)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rest.PostProcessResponse(w, cliCtx, res)
|
||||||
|
}
|
||||||
|
}
|
@ -1,116 +1,24 @@
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/types/rest"
|
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/kava-labs/kava/x/pricefeed/types"
|
|
||||||
tmtime "github.com/tendermint/tendermint/types/time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
restName = "assetCode"
|
restName = "marketID"
|
||||||
)
|
)
|
||||||
|
|
||||||
type postPriceReq struct {
|
type postPriceReq struct {
|
||||||
BaseReq rest.BaseReq `json:"base_req"`
|
BaseReq rest.BaseReq `json:"base_req"`
|
||||||
AssetCode string `json:"asset_code"`
|
MarketID string `json:"market_id"`
|
||||||
Price string `json:"price"`
|
Price string `json:"price"`
|
||||||
Expiry string `json:"expiry"`
|
Expiry string `json:"expiry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterRoutes - Central function to define routes that get registered by the main application
|
// RegisterRoutes - Central function to define routes that get registered by the main application
|
||||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) {
|
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||||
r.HandleFunc(fmt.Sprintf("/%s/rawprices", storeName), postPriceHandler(cliCtx)).Methods("PUT")
|
registerQueryRoutes(cliCtx, r)
|
||||||
r.HandleFunc(fmt.Sprintf("/%s/rawprices/{%s}", storeName, restName), getRawPricesHandler(cliCtx, storeName)).Methods("GET")
|
registerTxRoutes(cliCtx, r)
|
||||||
r.HandleFunc(fmt.Sprintf("/%s/currentprice/{%s}", storeName, restName), getCurrentPriceHandler(cliCtx, storeName)).Methods("GET")
|
|
||||||
r.HandleFunc(fmt.Sprintf("/%s/assets", storeName), getAssetsHandler(cliCtx, storeName)).Methods("GET")
|
|
||||||
}
|
|
||||||
|
|
||||||
func postPriceHandler(cliCtx context.CLIContext) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var req postPriceReq
|
|
||||||
|
|
||||||
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
baseReq := req.BaseReq.Sanitize()
|
|
||||||
if !baseReq.ValidateBasic(w) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
addr, err := sdk.AccAddressFromBech32(baseReq.From)
|
|
||||||
if err != nil {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
price, err := sdk.NewDecFromStr(req.Price)
|
|
||||||
if err != nil {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
expiryInt, ok := sdk.NewIntFromString(req.Expiry)
|
|
||||||
if !ok {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest, "invalid expiry")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
expiry := tmtime.Canonical(time.Unix(expiryInt.Int64(), 0))
|
|
||||||
|
|
||||||
// create the message
|
|
||||||
msg := types.NewMsgPostPrice(addr, req.AssetCode, price, expiry)
|
|
||||||
err = msg.ValidateBasic()
|
|
||||||
if err != nil {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRawPricesHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
paramType := vars[restName]
|
|
||||||
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/rawprices/%s", storeName, paramType), nil)
|
|
||||||
if err != nil {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rest.PostProcessResponse(w, cliCtx, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCurrentPriceHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
paramType := vars[restName]
|
|
||||||
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/price/%s", storeName, paramType), nil)
|
|
||||||
if err != nil {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rest.PostProcessResponse(w, cliCtx, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAssetsHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/assets/", storeName), nil)
|
|
||||||
if err != nil {
|
|
||||||
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rest.PostProcessResponse(w, cliCtx, res)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
66
x/pricefeed/client/rest/tx.go
Normal file
66
x/pricefeed/client/rest/tx.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/rest"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
|
||||||
|
"github.com/kava-labs/kava/x/pricefeed/types"
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||||
|
r.HandleFunc(fmt.Sprintf("/%s/rawprices", types.ModuleName), postPriceHandler(cliCtx)).Methods("PUT")
|
||||||
|
}
|
||||||
|
|
||||||
|
func postPriceHandler(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req postPriceReq
|
||||||
|
|
||||||
|
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
baseReq := req.BaseReq.Sanitize()
|
||||||
|
if !baseReq.ValidateBasic(w) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := sdk.AccAddressFromBech32(baseReq.From)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
price, err := sdk.NewDecFromStr(req.Price)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
expiryInt, ok := sdk.NewIntFromString(req.Expiry)
|
||||||
|
if !ok {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, "invalid expiry")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expiry := tmtime.Canonical(time.Unix(expiryInt.Int64(), 0))
|
||||||
|
|
||||||
|
// create the message
|
||||||
|
msg := types.NewMsgPostPrice(addr, req.MarketID, price, expiry)
|
||||||
|
err = msg.ValidateBasic()
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Package pricefeed allows a group of white-listed oracles to post price information of specific assets that are tracked by the system. For each asset, the module computes the median of all posted prices by white-listed oracles and takes that as the current price value.
|
Package pricefeed allows a group of white-listed oracles to post price information of specific markets that are tracked by the system. For each market, the module computes the median of all posted prices by white-listed oracles and takes that as the current price value.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package pricefeed
|
package pricefeed
|
||||||
|
@ -5,23 +5,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// InitGenesis sets distribution information for genesis.
|
// InitGenesis sets distribution information for genesis.
|
||||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) {
|
func InitGenesis(ctx sdk.Context, keeper Keeper, gs GenesisState) {
|
||||||
|
err := gs.Validate()
|
||||||
// Set the assets and oracles from params
|
if err != nil {
|
||||||
keeper.SetParams(ctx, data.Params)
|
panic(err)
|
||||||
|
}
|
||||||
|
// Set the markets and oracles from params
|
||||||
|
keeper.SetParams(ctx, gs.Params)
|
||||||
|
|
||||||
// Iterate through the posted prices and set them in the store
|
// Iterate through the posted prices and set them in the store
|
||||||
for _, pp := range data.PostedPrices {
|
for _, pp := range gs.PostedPrices {
|
||||||
_, err := keeper.SetPrice(ctx, pp.OracleAddress, pp.MarketID, pp.Price, pp.Expiry)
|
_, err := keeper.SetPrice(ctx, pp.OracleAddress, pp.MarketID, pp.Price, pp.Expiry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
params := keeper.GetParams(ctx)
|
||||||
|
|
||||||
// Set the current price (if any) based on what's now in the store
|
// Set the current price (if any) based on what's now in the store
|
||||||
for _, a := range data.Params.Markets {
|
for _, market := range params.Markets {
|
||||||
if a.Active {
|
if market.Active {
|
||||||
err := keeper.SetCurrentPrices(ctx, a.MarketID)
|
err := keeper.SetCurrentPrices(ctx, market.MarketID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -32,12 +36,12 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) {
|
|||||||
// ExportGenesis returns a GenesisState for a given context and keeper.
|
// ExportGenesis returns a GenesisState for a given context and keeper.
|
||||||
func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState {
|
func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState {
|
||||||
|
|
||||||
// Get the params for assets and oracles
|
// Get the params for markets and oracles
|
||||||
params := keeper.GetParams(ctx)
|
params := keeper.GetParams(ctx)
|
||||||
|
|
||||||
var postedPrices []PostedPrice
|
var postedPrices []PostedPrice
|
||||||
for _, asset := range keeper.GetMarketParams(ctx) {
|
for _, market := range keeper.GetMarkets(ctx) {
|
||||||
pp := keeper.GetRawPrices(ctx, asset.MarketID)
|
pp := keeper.GetRawPrices(ctx, market.MarketID)
|
||||||
postedPrices = append(postedPrices, pp...)
|
postedPrices = append(postedPrices, pp...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
x/pricefeed/genesis_test.go
Normal file
39
x/pricefeed/genesis_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package pricefeed_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
"github.com/kava-labs/kava/x/pricefeed"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GenesisTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
ctx sdk.Context
|
||||||
|
keeper pricefeed.Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *GenesisTestSuite) TestValidGenState() {
|
||||||
|
tApp := app.NewTestApp()
|
||||||
|
|
||||||
|
suite.NotPanics(func() {
|
||||||
|
tApp.InitializeFromGenesisStates(
|
||||||
|
NewPricefeedGenStateMulti(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
_, addrs := app.GeneratePrivKeyAddressPairs(10)
|
||||||
|
|
||||||
|
suite.NotPanics(func() {
|
||||||
|
tApp.InitializeFromGenesisStates(
|
||||||
|
NewPricefeedGenStateWithOracles(addrs),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenesisTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(GenesisTestSuite))
|
||||||
|
}
|
@ -28,11 +28,13 @@ func HandleMsgPostPrice(
|
|||||||
k Keeper,
|
k Keeper,
|
||||||
msg MsgPostPrice) sdk.Result {
|
msg MsgPostPrice) sdk.Result {
|
||||||
|
|
||||||
// TODO cleanup message validation and errors
|
_, err := k.GetOracle(ctx, msg.MarketID, msg.From)
|
||||||
_, err := k.GetOracle(ctx, msg.AssetCode, msg.From)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrInvalidOracle(k.Codespace()).Result()
|
return err.Result()
|
||||||
|
}
|
||||||
|
_, err = k.SetPrice(ctx, msg.From, msg.MarketID, msg.Price, msg.Expiry)
|
||||||
|
if err != nil {
|
||||||
|
err.Result()
|
||||||
}
|
}
|
||||||
k.SetPrice(ctx, msg.From, msg.AssetCode, msg.Price, msg.Expiry)
|
|
||||||
return sdk.Result{}
|
return sdk.Result{}
|
||||||
}
|
}
|
||||||
|
62
x/pricefeed/integration_test.go
Normal file
62
x/pricefeed/integration_test.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package pricefeed_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
"github.com/kava-labs/kava/x/pricefeed"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPricefeedGenStateMulti() app.GenesisState {
|
||||||
|
pfGenesis := pricefeed.GenesisState{
|
||||||
|
Params: pricefeed.Params{
|
||||||
|
Markets: []pricefeed.Market{
|
||||||
|
pricefeed.Market{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
|
pricefeed.Market{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PostedPrices: []pricefeed.PostedPrice{
|
||||||
|
pricefeed.PostedPrice{
|
||||||
|
MarketID: "btc:usd",
|
||||||
|
OracleAddress: sdk.AccAddress{},
|
||||||
|
Price: sdk.MustNewDecFromStr("8000.00"),
|
||||||
|
Expiry: time.Now().Add(1 * time.Hour),
|
||||||
|
},
|
||||||
|
pricefeed.PostedPrice{
|
||||||
|
MarketID: "xrp:usd",
|
||||||
|
OracleAddress: sdk.AccAddress{},
|
||||||
|
Price: sdk.MustNewDecFromStr("0.25"),
|
||||||
|
Expiry: time.Now().Add(1 * time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPricefeedGenStateWithOracles(addrs []sdk.AccAddress) app.GenesisState {
|
||||||
|
pfGenesis := pricefeed.GenesisState{
|
||||||
|
Params: pricefeed.Params{
|
||||||
|
Markets: []pricefeed.Market{
|
||||||
|
pricefeed.Market{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: addrs, Active: true},
|
||||||
|
pricefeed.Market{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: addrs, Active: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PostedPrices: []pricefeed.PostedPrice{
|
||||||
|
pricefeed.PostedPrice{
|
||||||
|
MarketID: "btc:usd",
|
||||||
|
OracleAddress: addrs[0],
|
||||||
|
Price: sdk.MustNewDecFromStr("8000.00"),
|
||||||
|
Expiry: time.Now().Add(1 * time.Hour),
|
||||||
|
},
|
||||||
|
pricefeed.PostedPrice{
|
||||||
|
MarketID: "xrp:usd",
|
||||||
|
OracleAddress: addrs[0],
|
||||||
|
Price: sdk.MustNewDecFromStr("0.25"),
|
||||||
|
Expiry: time.Now().Add(1 * time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
|
||||||
|
}
|
36
x/pricefeed/keeper/integration_test.go
Normal file
36
x/pricefeed/keeper/integration_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package keeper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
"github.com/kava-labs/kava/x/pricefeed"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPricefeedGenStateMulti() app.GenesisState {
|
||||||
|
pfGenesis := pricefeed.GenesisState{
|
||||||
|
Params: pricefeed.Params{
|
||||||
|
Markets: []pricefeed.Market{
|
||||||
|
pricefeed.Market{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
|
pricefeed.Market{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PostedPrices: []pricefeed.PostedPrice{
|
||||||
|
pricefeed.PostedPrice{
|
||||||
|
MarketID: "btc:usd",
|
||||||
|
OracleAddress: sdk.AccAddress{},
|
||||||
|
Price: sdk.MustNewDecFromStr("8000.00"),
|
||||||
|
Expiry: time.Now().Add(1 * time.Hour),
|
||||||
|
},
|
||||||
|
pricefeed.PostedPrice{
|
||||||
|
MarketID: "xrp:usd",
|
||||||
|
OracleAddress: sdk.AccAddress{},
|
||||||
|
Price: sdk.MustNewDecFromStr("0.25"),
|
||||||
|
Expiry: time.Now().Add(1 * time.Hour),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
|
||||||
|
}
|
@ -6,32 +6,30 @@ import (
|
|||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
"github.com/cosmos/cosmos-sdk/x/params/subspace"
|
||||||
|
|
||||||
"github.com/kava-labs/kava/x/pricefeed/types"
|
"github.com/kava-labs/kava/x/pricefeed/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Keeper struct for pricefeed module
|
// Keeper struct for pricefeed module
|
||||||
type Keeper struct {
|
type Keeper struct {
|
||||||
// The keys used to access the stores from Context
|
// key used to access the stores from Context
|
||||||
storeKey sdk.StoreKey
|
key sdk.StoreKey
|
||||||
// Codec for binary encoding/decoding
|
// Codec for binary encoding/decoding
|
||||||
cdc *codec.Codec
|
cdc *codec.Codec
|
||||||
// The reference to the Paramstore to get and set pricefeed specific params
|
// The reference to the Paramstore to get and set pricefeed specific params
|
||||||
paramstore params.Subspace
|
paramSubspace subspace.Subspace
|
||||||
// Reserved codespace
|
// Reserved codespace
|
||||||
codespace sdk.CodespaceType
|
codespace sdk.CodespaceType
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeeper returns a new keeper for the pricefeed module. It handles:
|
// NewKeeper returns a new keeper for the pricefeed module.
|
||||||
// - adding oracles
|
|
||||||
// - adding/removing assets from the pricefeed
|
|
||||||
func NewKeeper(
|
func NewKeeper(
|
||||||
cdc *codec.Codec, storeKey sdk.StoreKey, paramstore params.Subspace, codespace sdk.CodespaceType,
|
cdc *codec.Codec, key sdk.StoreKey, paramSubspace subspace.Subspace, codespace sdk.CodespaceType,
|
||||||
) Keeper {
|
) Keeper {
|
||||||
return Keeper{
|
return Keeper{
|
||||||
paramstore: paramstore.WithKeyTable(types.ParamKeyTable()),
|
paramSubspace: paramSubspace.WithKeyTable(types.ParamKeyTable()),
|
||||||
storeKey: storeKey,
|
key: key,
|
||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
codespace: codespace,
|
codespace: codespace,
|
||||||
}
|
}
|
||||||
@ -46,7 +44,7 @@ func (k Keeper) SetPrice(
|
|||||||
expiry time.Time) (types.PostedPrice, sdk.Error) {
|
expiry time.Time) (types.PostedPrice, sdk.Error) {
|
||||||
// If the expiry is less than or equal to the current blockheight, we consider the price valid
|
// If the expiry is less than or equal to the current blockheight, we consider the price valid
|
||||||
if expiry.After(ctx.BlockTime()) {
|
if expiry.After(ctx.BlockTime()) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.key)
|
||||||
prices := k.GetRawPrices(ctx, marketID)
|
prices := k.GetRawPrices(ctx, marketID)
|
||||||
var index int
|
var index int
|
||||||
found := false
|
found := false
|
||||||
@ -82,7 +80,7 @@ func (k Keeper) SetPrice(
|
|||||||
func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error {
|
func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error {
|
||||||
_, ok := k.GetMarket(ctx, marketID)
|
_, ok := k.GetMarket(ctx, marketID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return types.ErrInvalidAsset(k.codespace)
|
return types.ErrInvalidMarket(k.codespace, marketID)
|
||||||
}
|
}
|
||||||
prices := k.GetRawPrices(ctx, marketID)
|
prices := k.GetRawPrices(ctx, marketID)
|
||||||
var notExpiredPrices []types.CurrentPrice
|
var notExpiredPrices []types.CurrentPrice
|
||||||
@ -96,7 +94,7 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(notExpiredPrices) == 0 {
|
if len(notExpiredPrices) == 0 {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.key)
|
||||||
store.Set(
|
store.Set(
|
||||||
[]byte(types.CurrentPricePrefix+marketID), k.cdc.MustMarshalBinaryBare(types.CurrentPrice{}),
|
[]byte(types.CurrentPricePrefix+marketID), k.cdc.MustMarshalBinaryBare(types.CurrentPrice{}),
|
||||||
)
|
)
|
||||||
@ -104,7 +102,7 @@ func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error {
|
|||||||
}
|
}
|
||||||
medianPrice := k.CalculateMedianPrice(ctx, notExpiredPrices)
|
medianPrice := k.CalculateMedianPrice(ctx, notExpiredPrices)
|
||||||
|
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.key)
|
||||||
currentPrice := types.CurrentPrice{
|
currentPrice := types.CurrentPrice{
|
||||||
MarketID: marketID,
|
MarketID: marketID,
|
||||||
Price: medianPrice,
|
Price: medianPrice,
|
||||||
@ -144,9 +142,9 @@ func (k Keeper) calculateMeanPrice(ctx sdk.Context, prices []types.CurrentPrice)
|
|||||||
return mean
|
return mean
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentPrice fetches the current median price of all oracles for a specific asset
|
// GetCurrentPrice fetches the current median price of all oracles for a specific market
|
||||||
func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.CurrentPrice, sdk.Error) {
|
func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.CurrentPrice, sdk.Error) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.key)
|
||||||
bz := store.Get([]byte(types.CurrentPricePrefix + marketID))
|
bz := store.Get([]byte(types.CurrentPricePrefix + marketID))
|
||||||
|
|
||||||
if bz == nil {
|
if bz == nil {
|
||||||
@ -162,7 +160,7 @@ func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) (types.Current
|
|||||||
|
|
||||||
// GetRawPrices fetches the set of all prices posted by oracles for an asset
|
// GetRawPrices fetches the set of all prices posted by oracles for an asset
|
||||||
func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) []types.PostedPrice {
|
func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) []types.PostedPrice {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.key)
|
||||||
bz := store.Get([]byte(types.RawPriceFeedPrefix + marketID))
|
bz := store.Get([]byte(types.RawPriceFeedPrefix + marketID))
|
||||||
var prices []types.PostedPrice
|
var prices []types.PostedPrice
|
||||||
k.cdc.MustUnmarshalBinaryBare(bz, &prices)
|
k.cdc.MustUnmarshalBinaryBare(bz, &prices)
|
||||||
|
@ -20,11 +20,11 @@ func TestKeeper_SetGetMarket(t *testing.T) {
|
|||||||
|
|
||||||
mp := types.Params{
|
mp := types.Params{
|
||||||
Markets: types.Markets{
|
Markets: types.Markets{
|
||||||
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
|
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
keeper.SetParams(ctx, mp)
|
keeper.SetParams(ctx, mp)
|
||||||
markets := keeper.GetMarketParams(ctx)
|
markets := keeper.GetMarkets(ctx)
|
||||||
require.Equal(t, len(markets), 1)
|
require.Equal(t, len(markets), 1)
|
||||||
require.Equal(t, markets[0].MarketID, "tstusd")
|
require.Equal(t, markets[0].MarketID, "tstusd")
|
||||||
|
|
||||||
@ -33,12 +33,12 @@ func TestKeeper_SetGetMarket(t *testing.T) {
|
|||||||
|
|
||||||
mp = types.Params{
|
mp = types.Params{
|
||||||
Markets: types.Markets{
|
Markets: types.Markets{
|
||||||
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
|
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
types.Market{MarketID: "tst2usd", BaseAsset: "tst2", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
|
types.Market{MarketID: "tst2usd", BaseAsset: "tst2", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
keeper.SetParams(ctx, mp)
|
keeper.SetParams(ctx, mp)
|
||||||
markets = keeper.GetMarketParams(ctx)
|
markets = keeper.GetMarkets(ctx)
|
||||||
require.Equal(t, len(markets), 2)
|
require.Equal(t, len(markets), 2)
|
||||||
require.Equal(t, markets[0].MarketID, "tstusd")
|
require.Equal(t, markets[0].MarketID, "tstusd")
|
||||||
require.Equal(t, markets[1].MarketID, "tst2usd")
|
require.Equal(t, markets[1].MarketID, "tst2usd")
|
||||||
@ -56,7 +56,7 @@ func TestKeeper_GetSetPrice(t *testing.T) {
|
|||||||
|
|
||||||
mp := types.Params{
|
mp := types.Params{
|
||||||
Markets: types.Markets{
|
Markets: types.Markets{
|
||||||
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
|
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
keeper.SetParams(ctx, mp)
|
keeper.SetParams(ctx, mp)
|
||||||
@ -100,7 +100,7 @@ func TestKeeper_GetSetCurrentPrice(t *testing.T) {
|
|||||||
|
|
||||||
mp := types.Params{
|
mp := types.Params{
|
||||||
Markets: types.Markets{
|
Markets: types.Markets{
|
||||||
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
|
types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
keeper.SetParams(ctx, mp)
|
keeper.SetParams(ctx, mp)
|
||||||
|
@ -1,58 +1,56 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
"github.com/kava-labs/kava/x/pricefeed/types"
|
"github.com/kava-labs/kava/x/pricefeed/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetParams gets params from the store
|
// GetParams returns the params from the store
|
||||||
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
|
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
|
||||||
return types.NewParams(k.GetMarketParams(ctx))
|
var p types.Params
|
||||||
|
k.paramSubspace.GetParamSet(ctx, &p)
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetParams updates params in the store
|
// SetParams sets params on the store
|
||||||
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||||
k.paramstore.SetParamSet(ctx, ¶ms)
|
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMarketParams get asset params from store
|
// GetMarkets returns the markets from params
|
||||||
func (k Keeper) GetMarketParams(ctx sdk.Context) types.Markets {
|
func (k Keeper) GetMarkets(ctx sdk.Context) types.Markets {
|
||||||
var markets types.Markets
|
return k.GetParams(ctx).Markets
|
||||||
k.paramstore.Get(ctx, types.KeyMarkets, &markets)
|
|
||||||
return markets
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOracles returns the oracles in the pricefeed store
|
// GetOracles returns the oracles in the pricefeed store
|
||||||
func (k Keeper) GetOracles(ctx sdk.Context, marketID string) (types.Oracles, error) {
|
func (k Keeper) GetOracles(ctx sdk.Context, marketID string) ([]sdk.AccAddress, sdk.Error) {
|
||||||
|
|
||||||
for _, m := range k.GetMarketParams(ctx) {
|
for _, m := range k.GetMarkets(ctx) {
|
||||||
if marketID == m.MarketID {
|
if marketID == m.MarketID {
|
||||||
return m.Oracles, nil
|
return m.Oracles, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types.Oracles{}, fmt.Errorf("asset %s not found", marketID)
|
return []sdk.AccAddress{}, types.ErrInvalidMarket(k.Codespace(), marketID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOracle returns the oracle from the store or an error if not found
|
// GetOracle returns the oracle from the store or an error if not found
|
||||||
func (k Keeper) GetOracle(ctx sdk.Context, marketID string, address sdk.AccAddress) (types.Oracle, error) {
|
func (k Keeper) GetOracle(ctx sdk.Context, marketID string, address sdk.AccAddress) (sdk.AccAddress, sdk.Error) {
|
||||||
oracles, err := k.GetOracles(ctx, marketID)
|
oracles, err := k.GetOracles(ctx, marketID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.Oracle{}, fmt.Errorf("asset %s not found", marketID)
|
return sdk.AccAddress{}, types.ErrInvalidMarket(k.Codespace(), marketID)
|
||||||
}
|
}
|
||||||
for _, o := range oracles {
|
for _, addr := range oracles {
|
||||||
if address.Equals(o.Address) {
|
if address.Equals(addr) {
|
||||||
return o, nil
|
return addr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types.Oracle{}, fmt.Errorf("oracle %s not found for asset %s", address, marketID)
|
return sdk.AccAddress{}, types.ErrInvalidOracle(k.codespace, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMarket returns the market if it is in the pricefeed system
|
// GetMarket returns the market if it is in the pricefeed system
|
||||||
func (k Keeper) GetMarket(ctx sdk.Context, marketID string) (types.Market, bool) {
|
func (k Keeper) GetMarket(ctx sdk.Context, marketID string) (types.Market, bool) {
|
||||||
markets := k.GetMarketParams(ctx)
|
markets := k.GetMarkets(ctx)
|
||||||
|
|
||||||
for i := range markets {
|
for i := range markets {
|
||||||
if markets[i].MarketID == marketID {
|
if markets[i].MarketID == marketID {
|
||||||
|
49
x/pricefeed/keeper/params_test.go
Normal file
49
x/pricefeed/keeper/params_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package keeper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
"github.com/kava-labs/kava/x/pricefeed/keeper"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeeperTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
keeper keeper.Keeper
|
||||||
|
addrs []sdk.AccAddress
|
||||||
|
app app.TestApp
|
||||||
|
ctx sdk.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *KeeperTestSuite) SetupTest() {
|
||||||
|
tApp := app.NewTestApp()
|
||||||
|
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||||
|
_, addrs := app.GeneratePrivKeyAddressPairs(10)
|
||||||
|
tApp.InitializeFromGenesisStates(
|
||||||
|
NewPricefeedGenStateMulti(),
|
||||||
|
)
|
||||||
|
suite.keeper = tApp.GetPriceFeedKeeper()
|
||||||
|
suite.ctx = ctx
|
||||||
|
suite.addrs = addrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *KeeperTestSuite) TestGetSetOracles() {
|
||||||
|
params := suite.keeper.GetParams(suite.ctx)
|
||||||
|
suite.Equal([]sdk.AccAddress(nil), params.Markets[0].Oracles)
|
||||||
|
params.Markets[0].Oracles = suite.addrs
|
||||||
|
suite.NotPanics(func() { suite.keeper.SetParams(suite.ctx, params) })
|
||||||
|
params = suite.keeper.GetParams(suite.ctx)
|
||||||
|
suite.Equal(suite.addrs, params.Markets[0].Oracles)
|
||||||
|
addr, err := suite.keeper.GetOracle(suite.ctx, params.Markets[0].MarketID, suite.addrs[0])
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(suite.addrs[0], addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeeperTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(KeeperTestSuite))
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
@ -14,11 +16,11 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
|||||||
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
||||||
switch path[0] {
|
switch path[0] {
|
||||||
case types.QueryCurrentPrice:
|
case types.QueryCurrentPrice:
|
||||||
return queryCurrentPrice(ctx, path[1:], req, keeper)
|
return queryCurrentPrice(ctx, req, keeper)
|
||||||
case types.QueryRawPrices:
|
case types.QueryRawPrices:
|
||||||
return queryRawPrices(ctx, path[1:], req, keeper)
|
return queryRawPrices(ctx, req, keeper)
|
||||||
case types.QueryAssets:
|
case types.QueryMarkets:
|
||||||
return queryAssets(ctx, req, keeper)
|
return queryMarkets(ctx, req, keeper)
|
||||||
default:
|
default:
|
||||||
return nil, sdk.ErrUnknownRequest("unknown pricefeed query endpoint")
|
return nil, sdk.ErrUnknownRequest("unknown pricefeed query endpoint")
|
||||||
}
|
}
|
||||||
@ -26,51 +28,53 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryCurrentPrice(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
|
func queryCurrentPrice(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) {
|
||||||
marketID := path[0]
|
var requestParams types.QueryPricesParams
|
||||||
_, found := keeper.GetMarket(ctx, marketID)
|
err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams)
|
||||||
if !found {
|
|
||||||
return []byte{}, sdk.ErrUnknownRequest("asset not found")
|
|
||||||
}
|
|
||||||
currentPrice, err := keeper.GetCurrentPrice(ctx, marketID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
|
||||||
}
|
}
|
||||||
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, currentPrice)
|
_, found := keeper.GetMarket(ctx, requestParams.MarketID)
|
||||||
if err2 != nil {
|
|
||||||
panic("could not marshal result to JSON")
|
|
||||||
}
|
|
||||||
|
|
||||||
return bz, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryRawPrices(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
|
|
||||||
var priceList types.QueryRawPricesResp
|
|
||||||
marketID := path[0]
|
|
||||||
_, found := keeper.GetMarket(ctx, marketID)
|
|
||||||
if !found {
|
if !found {
|
||||||
return []byte{}, sdk.ErrUnknownRequest("asset not found")
|
return []byte{}, sdk.ErrUnknownRequest("asset not found")
|
||||||
}
|
}
|
||||||
rawPrices := keeper.GetRawPrices(ctx, marketID)
|
currentPrice, sdkErr := keeper.GetCurrentPrice(ctx, requestParams.MarketID)
|
||||||
for _, price := range rawPrices {
|
if sdkErr != nil {
|
||||||
priceList = append(priceList, price.String())
|
return nil, sdkErr
|
||||||
}
|
}
|
||||||
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, priceList)
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, currentPrice)
|
||||||
if err2 != nil {
|
if err != nil {
|
||||||
panic("could not marshal result to JSON")
|
panic("could not marshal result to JSON")
|
||||||
}
|
}
|
||||||
|
|
||||||
return bz, nil
|
return bz, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryAssets(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
|
func queryRawPrices(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) {
|
||||||
var assetList types.QueryAssetsResp
|
var requestParams types.QueryPricesParams
|
||||||
assets := keeper.GetMarketParams(ctx)
|
err := keeper.cdc.UnmarshalJSON(req.Data, &requestParams)
|
||||||
for _, asset := range assets {
|
if err != nil {
|
||||||
assetList = append(assetList, asset.String())
|
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
|
||||||
}
|
}
|
||||||
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, assetList)
|
_, found := keeper.GetMarket(ctx, requestParams.MarketID)
|
||||||
if err2 != nil {
|
if !found {
|
||||||
|
return []byte{}, sdk.ErrUnknownRequest("asset not found")
|
||||||
|
}
|
||||||
|
rawPrices := keeper.GetRawPrices(ctx, requestParams.MarketID)
|
||||||
|
|
||||||
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, rawPrices)
|
||||||
|
if err != nil {
|
||||||
|
panic("could not marshal result to JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bz, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryMarkets(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, sdkErr sdk.Error) {
|
||||||
|
markets := keeper.GetMarkets(ctx)
|
||||||
|
|
||||||
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, markets)
|
||||||
|
if err != nil {
|
||||||
panic("could not marshal result to JSON")
|
panic("could not marshal result to JSON")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ var (
|
|||||||
_ module.AppModule = AppModule{}
|
_ module.AppModule = AppModule{}
|
||||||
_ module.AppModuleBasic = AppModuleBasic{}
|
_ module.AppModuleBasic = AppModuleBasic{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppModuleBasic app module basics object
|
// AppModuleBasic app module basics object
|
||||||
type AppModuleBasic struct{}
|
type AppModuleBasic struct{}
|
||||||
|
|
||||||
@ -40,17 +41,17 @@ func (AppModuleBasic) DefaultGenesis() json.RawMessage {
|
|||||||
|
|
||||||
// ValidateGenesis module validate genesis
|
// ValidateGenesis module validate genesis
|
||||||
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
|
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
|
||||||
var data GenesisState
|
var gs GenesisState
|
||||||
err := ModuleCdc.UnmarshalJSON(bz, &data)
|
err := ModuleCdc.UnmarshalJSON(bz, &gs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ValidateGenesis(data)
|
return gs.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterRESTRoutes register rest routes
|
// RegisterRESTRoutes register rest routes
|
||||||
func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
|
func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
|
||||||
rest.RegisterRoutes(ctx, rtr, StoreKey)
|
rest.RegisterRoutes(ctx, rtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTxCmd get the root tx command of this module
|
// GetTxCmd get the root tx command of this module
|
||||||
|
@ -37,12 +37,12 @@ func ErrNoValidPrice(codespace sdk.CodespaceType) sdk.Error {
|
|||||||
return sdk.NewError(codespace, CodeInvalidPrice, fmt.Sprintf("All input prices are expired."))
|
return sdk.NewError(codespace, CodeInvalidPrice, fmt.Sprintf("All input prices are expired."))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrInvalidAsset Error constructor for posted price messages for invalid assets
|
// ErrInvalidAsset Error constructor for posted price messages for invalid markets
|
||||||
func ErrInvalidAsset(codespace sdk.CodespaceType) sdk.Error {
|
func ErrInvalidMarket(codespace sdk.CodespaceType, marketId string) sdk.Error {
|
||||||
return sdk.NewError(codespace, CodeInvalidAsset, fmt.Sprintf("Asset code does not exist."))
|
return sdk.NewError(codespace, CodeInvalidAsset, fmt.Sprintf("market %s does not exist", marketId))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrInvalidOracle Error constructor for posted price messages for invalid oracles
|
// ErrInvalidOracle Error constructor for posted price messages for invalid oracles
|
||||||
func ErrInvalidOracle(codespace sdk.CodespaceType) sdk.Error {
|
func ErrInvalidOracle(codespace sdk.CodespaceType, addr sdk.AccAddress) sdk.Error {
|
||||||
return sdk.NewError(codespace, CodeInvalidOracle, fmt.Sprintf("Oracle does not exist or not authorized."))
|
return sdk.NewError(codespace, CodeInvalidOracle, fmt.Sprintf("oracle %s does not exist or not authorized", addr))
|
||||||
}
|
}
|
||||||
|
@ -27,22 +27,22 @@ func DefaultGenesisState() GenesisState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Equal checks whether two gov GenesisState structs are equivalent
|
// Equal checks whether two gov GenesisState structs are equivalent
|
||||||
func (data GenesisState) Equal(data2 GenesisState) bool {
|
func (gs GenesisState) Equal(gs2 GenesisState) bool {
|
||||||
b1 := ModuleCdc.MustMarshalBinaryBare(data)
|
b1 := ModuleCdc.MustMarshalBinaryBare(gs)
|
||||||
b2 := ModuleCdc.MustMarshalBinaryBare(data2)
|
b2 := ModuleCdc.MustMarshalBinaryBare(gs2)
|
||||||
return bytes.Equal(b1, b2)
|
return bytes.Equal(b1, b2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmpty returns true if a GenesisState is empty
|
// IsEmpty returns true if a GenesisState is empty
|
||||||
func (data GenesisState) IsEmpty() bool {
|
func (gs GenesisState) IsEmpty() bool {
|
||||||
return data.Equal(GenesisState{})
|
return gs.Equal(GenesisState{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateGenesis performs basic validation of genesis data returning an
|
// ValidateGenesis performs basic validation of genesis data returning an
|
||||||
// error for any failed validation criteria.
|
// error for any failed validation criteria.
|
||||||
func ValidateGenesis(data GenesisState) error {
|
func (gs GenesisState) Validate() error {
|
||||||
|
|
||||||
if err := data.Params.Validate(); err != nil {
|
if err := gs.Params.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -22,8 +22,8 @@ const (
|
|||||||
// CurrentPricePrefix prefix for the current price of an asset
|
// CurrentPricePrefix prefix for the current price of an asset
|
||||||
CurrentPricePrefix = StoreKey + ":currentprice:"
|
CurrentPricePrefix = StoreKey + ":currentprice:"
|
||||||
|
|
||||||
// AssetPrefix Prefix for the assets in the pricefeed system
|
// MarketPrefix Prefix for the assets in the pricefeed system
|
||||||
AssetPrefix = StoreKey + ":assets"
|
MarketPrefix = StoreKey + ":markets"
|
||||||
|
|
||||||
// OraclePrefix store prefix for the oracle accounts
|
// OraclePrefix store prefix for the oracle accounts
|
||||||
OraclePrefix = StoreKey + ":oracles"
|
OraclePrefix = StoreKey + ":oracles"
|
||||||
|
@ -8,12 +8,12 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Market struct that represents an asset in the pricefeed
|
// Market an asset in the pricefeed
|
||||||
type Market struct {
|
type Market struct {
|
||||||
MarketID string `json:"market_id" yaml:"market_id"`
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
BaseAsset string `json:"base_asset" yaml:"base_asset"`
|
BaseAsset string `json:"base_asset" yaml:"base_asset"`
|
||||||
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
|
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
|
||||||
Oracles Oracles `json:"oracles" yaml:"oracles"`
|
Oracles []sdk.AccAddress `json:"oracles" yaml:"oracles"`
|
||||||
Active bool `json:"active" yaml:"active"`
|
Active bool `json:"active" yaml:"active"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,35 +40,13 @@ func (ms Markets) String() string {
|
|||||||
return strings.TrimSpace(out)
|
return strings.TrimSpace(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oracle struct that documents which address an oracle is using
|
// CurrentPrice struct that contains the metadata of a current price for a particular market in the pricefeed module.
|
||||||
type Oracle struct {
|
|
||||||
Address sdk.AccAddress `json:"address" yaml:"address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements fmt.Stringer
|
|
||||||
func (o Oracle) String() string {
|
|
||||||
return fmt.Sprintf(`Address: %s`, o.Address)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Oracles array type for oracle
|
|
||||||
type Oracles []Oracle
|
|
||||||
|
|
||||||
// String implements fmt.Stringer
|
|
||||||
func (os Oracles) String() string {
|
|
||||||
out := "Oracles:\n"
|
|
||||||
for _, o := range os {
|
|
||||||
out += fmt.Sprintf("%s\n", o.String())
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CurrentPrice struct that contains the metadata of a current price for a particular asset in the pricefeed module.
|
|
||||||
type CurrentPrice struct {
|
type CurrentPrice struct {
|
||||||
MarketID string `json:"market_id" yaml:"market_id"`
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
Price sdk.Dec `json:"price" yaml:"price"`
|
Price sdk.Dec `json:"price" yaml:"price"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostedPrice struct represented a price for an asset posted by a specific oracle
|
// PostedPrice price for market posted by a specific oracle
|
||||||
type PostedPrice struct {
|
type PostedPrice struct {
|
||||||
MarketID string `json:"market_id" yaml:"market_id"`
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
OracleAddress sdk.AccAddress `json:"oracle_address" yaml:"oracle_address"`
|
OracleAddress sdk.AccAddress `json:"oracle_address" yaml:"oracle_address"`
|
@ -15,7 +15,7 @@ const (
|
|||||||
// Used by oracles to input prices to the pricefeed
|
// Used by oracles to input prices to the pricefeed
|
||||||
type MsgPostPrice struct {
|
type MsgPostPrice struct {
|
||||||
From sdk.AccAddress // client that sent in this address
|
From sdk.AccAddress // client that sent in this address
|
||||||
AssetCode string // asset code used by exchanges/api
|
MarketID string // asset code used by exchanges/api
|
||||||
Price sdk.Dec // price in decimal (max precision 18)
|
Price sdk.Dec // price in decimal (max precision 18)
|
||||||
Expiry time.Time // expiry time
|
Expiry time.Time // expiry time
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ func NewMsgPostPrice(
|
|||||||
expiry time.Time) MsgPostPrice {
|
expiry time.Time) MsgPostPrice {
|
||||||
return MsgPostPrice{
|
return MsgPostPrice{
|
||||||
From: from,
|
From: from,
|
||||||
AssetCode: assetCode,
|
MarketID: assetCode,
|
||||||
Price: price,
|
Price: price,
|
||||||
Expiry: expiry,
|
Expiry: expiry,
|
||||||
}
|
}
|
||||||
@ -54,10 +54,10 @@ func (msg MsgPostPrice) GetSigners() []sdk.AccAddress {
|
|||||||
// ValidateBasic does a simple validation check that doesn't require access to any other information.
|
// ValidateBasic does a simple validation check that doesn't require access to any other information.
|
||||||
func (msg MsgPostPrice) ValidateBasic() sdk.Error {
|
func (msg MsgPostPrice) ValidateBasic() sdk.Error {
|
||||||
if msg.From.Empty() {
|
if msg.From.Empty() {
|
||||||
return sdk.ErrInternal("invalid (empty) bidder address")
|
return sdk.ErrInternal("invalid (empty) from address")
|
||||||
}
|
}
|
||||||
if len(msg.AssetCode) == 0 {
|
if len(msg.MarketID) == 0 {
|
||||||
return sdk.ErrInternal("invalid (empty) asset code")
|
return sdk.ErrInternal("invalid (empty) market id")
|
||||||
}
|
}
|
||||||
if msg.Price.LT(sdk.ZeroDec()) {
|
if msg.Price.LT(sdk.ZeroDec()) {
|
||||||
return sdk.ErrInternal("invalid (negative) price")
|
return sdk.ErrInternal("invalid (negative) price")
|
||||||
|
@ -4,33 +4,20 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/params"
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Parameter keys
|
||||||
var (
|
var (
|
||||||
// KeyMarkets store key for markets
|
|
||||||
KeyMarkets = []byte("Markets")
|
KeyMarkets = []byte("Markets")
|
||||||
|
DefaultMarkets = Markets{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParamKeyTable Key declaration for parameters
|
|
||||||
func ParamKeyTable() params.KeyTable {
|
|
||||||
return params.NewKeyTable().RegisterParamSet(&Params{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Params params for pricefeed. Can be altered via governance
|
// Params params for pricefeed. Can be altered via governance
|
||||||
type Params struct {
|
type Params struct {
|
||||||
Markets Markets `json:"markets" yaml:"markets"` // Array containing the markets supported by the pricefeed
|
Markets Markets `json:"markets" yaml:"markets"` // Array containing the markets supported by the pricefeed
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
|
|
||||||
// pairs of pricefeed module's parameters.
|
|
||||||
func (p Params) ParamSetPairs() params.ParamSetPairs {
|
|
||||||
return params.ParamSetPairs{
|
|
||||||
{Key: KeyMarkets, Value: &p.Markets},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewParams creates a new AssetParams object
|
// NewParams creates a new AssetParams object
|
||||||
func NewParams(markets Markets) Params {
|
func NewParams(markets Markets) Params {
|
||||||
return Params{
|
return Params{
|
||||||
@ -40,24 +27,31 @@ func NewParams(markets Markets) Params {
|
|||||||
|
|
||||||
// DefaultParams default params for pricefeed
|
// DefaultParams default params for pricefeed
|
||||||
func DefaultParams() Params {
|
func DefaultParams() Params {
|
||||||
return NewParams(Markets{})
|
return NewParams(DefaultMarkets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamKeyTable Key declaration for parameters
|
||||||
|
func ParamKeyTable() params.KeyTable {
|
||||||
|
return params.NewKeyTable().RegisterParamSet(&Params{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
|
||||||
|
// pairs of pricefeed module's parameters.
|
||||||
|
func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||||
|
return params.ParamSetPairs{
|
||||||
|
{Key: KeyMarkets, Value: &p.Markets},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements fmt.stringer
|
// String implements fmt.stringer
|
||||||
func (p Params) String() string {
|
func (p Params) String() string {
|
||||||
out := "Params:\n"
|
out := "Params:\n"
|
||||||
for _, a := range p.Markets {
|
for _, a := range p.Markets {
|
||||||
out += a.String()
|
out += fmt.Sprintf("%s\n", a.String())
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(out)
|
return strings.TrimSpace(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParamSubspace defines the expected Subspace interface for parameters
|
|
||||||
type ParamSubspace interface {
|
|
||||||
Get(ctx sdk.Context, key []byte, ptr interface{})
|
|
||||||
Set(ctx sdk.Context, key []byte, param interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate ensure that params have valid values
|
// Validate ensure that params have valid values
|
||||||
func (p Params) Validate() error {
|
func (p Params) Validate() error {
|
||||||
// iterate over assets and verify them
|
// iterate over assets and verify them
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// price Takes an [assetcode] and returns CurrentPrice for that asset
|
// price Takes an [assetcode] and returns CurrentPrice for that asset
|
||||||
// pricefeed Takes an [assetcode] and returns the raw []PostedPrice for that asset
|
// pricefeed Takes an [assetcode] and returns the raw []PostedPrice for that asset
|
||||||
// assets Returns []Assets in the pricefeed system
|
// assets Returns []Assets in the pricefeed system
|
||||||
@ -13,23 +9,11 @@ const (
|
|||||||
QueryCurrentPrice = "price"
|
QueryCurrentPrice = "price"
|
||||||
// QueryRawPrices command for raw price queries
|
// QueryRawPrices command for raw price queries
|
||||||
QueryRawPrices = "rawprices"
|
QueryRawPrices = "rawprices"
|
||||||
// QueryAssets command for assets query
|
// QueryMarkets command for assets query
|
||||||
QueryAssets = "assets"
|
QueryMarkets = "markets"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueryRawPricesResp response to a rawprice query
|
// QueryPricesParams fields for querying prices
|
||||||
type QueryRawPricesResp []string
|
type QueryPricesParams struct {
|
||||||
|
MarketID string
|
||||||
// implement fmt.Stringer
|
|
||||||
func (n QueryRawPricesResp) String() string {
|
|
||||||
return strings.Join(n[:], "\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryAssetsResp response to a assets query
|
|
||||||
type QueryAssetsResp []string
|
|
||||||
|
|
||||||
// implement fmt.Stringer
|
|
||||||
func (n QueryAssetsResp) String() string {
|
|
||||||
return strings.Join(n[:], "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user