address review comments for pricefeed

This commit is contained in:
Kevin Davis 2019-12-04 11:32:08 -05:00
parent 1a9b8514c9
commit bf83a9bf8f
10 changed files with 195 additions and 169 deletions

View File

@ -1,17 +1,25 @@
package pricefeed package pricefeed
import ( import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// 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.GetAssetParams(ctx) { for _, a := range k.GetMarketParams(ctx) {
if a.Active { if a.Active {
err := k.SetCurrentPrices(ctx, a.AssetCode) err := k.SetCurrentPrices(ctx, a.MarketID)
if err != nil { if err != nil {
// TODO emit an event that price failed to update // In the event of failure, emit an event.
ctx.EventManager().EmitEvent(
sdk.NewEvent(
EventTypeNoValidPrices,
sdk.NewAttribute(AttributeKeyPriceUpdateFailed, fmt.Sprintf("%s", a.MarketID)),
),
)
continue continue
} }
} }

View File

@ -11,25 +11,27 @@ import (
) )
const ( const (
DefaultCodespace = types.DefaultCodespace DefaultCodespace = types.DefaultCodespace
CodeEmptyInput = types.CodeEmptyInput CodeEmptyInput = types.CodeEmptyInput
CodeExpired = types.CodeExpired CodeExpired = types.CodeExpired
CodeInvalidPrice = types.CodeInvalidPrice CodeInvalidPrice = types.CodeInvalidPrice
CodeInvalidAsset = types.CodeInvalidAsset CodeInvalidAsset = types.CodeInvalidAsset
CodeInvalidOracle = types.CodeInvalidOracle CodeInvalidOracle = types.CodeInvalidOracle
ModuleName = types.ModuleName EventTypeNoValidPrices = types.EventTypeNoValidPrices
StoreKey = types.StoreKey AttributeKeyPriceUpdateFailed = types.AttributeKeyPriceUpdateFailed
RouterKey = types.RouterKey ModuleName = types.ModuleName
QuerierRoute = types.QuerierRoute StoreKey = types.StoreKey
DefaultParamspace = types.DefaultParamspace RouterKey = types.RouterKey
RawPriceFeedPrefix = types.RawPriceFeedPrefix QuerierRoute = types.QuerierRoute
CurrentPricePrefix = types.CurrentPricePrefix DefaultParamspace = types.DefaultParamspace
AssetPrefix = types.AssetPrefix RawPriceFeedPrefix = types.RawPriceFeedPrefix
OraclePrefix = types.OraclePrefix CurrentPricePrefix = types.CurrentPricePrefix
TypeMsgPostPrice = types.TypeMsgPostPrice AssetPrefix = types.AssetPrefix
QueryCurrentPrice = types.QueryCurrentPrice OraclePrefix = types.OraclePrefix
QueryRawPrices = types.QueryRawPrices TypeMsgPostPrice = types.TypeMsgPostPrice
QueryAssets = types.QueryAssets QueryCurrentPrice = types.QueryCurrentPrice
QueryRawPrices = types.QueryRawPrices
QueryAssets = types.QueryAssets
) )
var ( var (
@ -51,23 +53,23 @@ var (
NewQuerier = keeper.NewQuerier NewQuerier = keeper.NewQuerier
// variable aliases // variable aliases
ModuleCdc = types.ModuleCdc ModuleCdc = types.ModuleCdc
KeyAssets = types.KeyAssets KeyMarkets = types.KeyMarkets
) )
type ( type (
Market = types.Market
Markets = types.Markets
Oracle = types.Oracle
Oracles = types.Oracles
CurrentPrice = types.CurrentPrice
PostedPrice = types.PostedPrice
SortDecs = types.SortDecs
GenesisState = types.GenesisState GenesisState = types.GenesisState
MsgPostPrice = types.MsgPostPrice MsgPostPrice = types.MsgPostPrice
Params = types.Params Params = types.Params
ParamSubspace = types.ParamSubspace ParamSubspace = types.ParamSubspace
QueryRawPricesResp = types.QueryRawPricesResp QueryRawPricesResp = types.QueryRawPricesResp
QueryAssetsResp = types.QueryAssetsResp QueryAssetsResp = types.QueryAssetsResp
Asset = types.Asset
Assets = types.Assets
Oracle = types.Oracle
Oracles = types.Oracles
CurrentPrice = types.CurrentPrice
PostedPrice = types.PostedPrice
SortDecs = types.SortDecs
Keeper = keeper.Keeper Keeper = keeper.Keeper
) )

View File

@ -12,16 +12,16 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) {
// 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 data.PostedPrices {
_, err := keeper.SetPrice(ctx, pp.OracleAddress, pp.AssetCode, 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)
} }
} }
// 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.Assets { for _, a := range data.Params.Markets {
if a.Active { if a.Active {
_ = keeper.SetCurrentPrices(ctx, a.AssetCode) _ = keeper.SetCurrentPrices(ctx, a.MarketID)
} }
} }
} }
@ -33,8 +33,8 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState {
params := keeper.GetParams(ctx) params := keeper.GetParams(ctx)
var postedPrices []PostedPrice var postedPrices []PostedPrice
for _, asset := range keeper.GetAssetParams(ctx) { for _, asset := range keeper.GetMarketParams(ctx) {
pp := keeper.GetRawPrices(ctx, asset.AssetCode) pp := keeper.GetRawPrices(ctx, asset.MarketID)
postedPrices = append(postedPrices, pp...) postedPrices = append(postedPrices, pp...)
} }

View File

@ -41,13 +41,13 @@ func NewKeeper(
func (k Keeper) SetPrice( func (k Keeper) SetPrice(
ctx sdk.Context, ctx sdk.Context,
oracle sdk.AccAddress, oracle sdk.AccAddress,
assetCode string, marketID string,
price sdk.Dec, price sdk.Dec,
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.storeKey)
prices := k.GetRawPrices(ctx, assetCode) prices := k.GetRawPrices(ctx, marketID)
var index int var index int
found := false found := false
for i := range prices { for i := range prices {
@ -60,17 +60,17 @@ func (k Keeper) SetPrice(
// set the price for that particular oracle // set the price for that particular oracle
if found { if found {
prices[index] = types.PostedPrice{ prices[index] = types.PostedPrice{
AssetCode: assetCode, OracleAddress: oracle, MarketID: marketID, OracleAddress: oracle,
Price: price, Expiry: expiry} Price: price, Expiry: expiry}
} else { } else {
prices = append(prices, types.PostedPrice{ prices = append(prices, types.PostedPrice{
AssetCode: assetCode, OracleAddress: oracle, MarketID: marketID, OracleAddress: oracle,
Price: price, Expiry: expiry}) Price: price, Expiry: expiry})
index = len(prices) - 1 index = len(prices) - 1
} }
store.Set( store.Set(
[]byte(types.RawPriceFeedPrefix+assetCode), k.cdc.MustMarshalBinaryBare(prices), []byte(types.RawPriceFeedPrefix+marketID), k.cdc.MustMarshalBinaryBare(prices),
) )
return prices[index], nil return prices[index], nil
} }
@ -79,67 +79,74 @@ func (k Keeper) SetPrice(
} }
// SetCurrentPrices updates the price of an asset to the meadian of all valid oracle inputs // SetCurrentPrices updates the price of an asset to the meadian of all valid oracle inputs
func (k Keeper) SetCurrentPrices(ctx sdk.Context, assetCode string) sdk.Error { func (k Keeper) SetCurrentPrices(ctx sdk.Context, marketID string) sdk.Error {
_, ok := k.GetAsset(ctx, assetCode) _, ok := k.GetMarket(ctx, marketID)
if !ok { if !ok {
return types.ErrInvalidAsset(k.codespace) return types.ErrInvalidAsset(k.codespace)
} }
prices := k.GetRawPrices(ctx, assetCode) prices := k.GetRawPrices(ctx, marketID)
var notExpiredPrices []types.CurrentPrice var notExpiredPrices []types.CurrentPrice
// filter out expired prices // filter out expired prices
for _, v := range prices { for _, v := range prices {
if v.Expiry.After(ctx.BlockTime()) { if v.Expiry.After(ctx.BlockTime()) {
notExpiredPrices = append(notExpiredPrices, types.CurrentPrice{ notExpiredPrices = append(notExpiredPrices, types.CurrentPrice{
AssetCode: v.AssetCode, MarketID: v.MarketID,
Price: v.Price, Price: v.Price,
}) })
} }
} }
l := len(notExpiredPrices) medianPrice, err := k.CalculateMedianPrice(ctx, notExpiredPrices)
var medianPrice sdk.Dec if err != nil {
// TODO make threshold for acceptance (ie. require 51% of oracles to have posted valid prices return err
if l == 0 {
// Error if there are no valid prices in the raw pricefeed
return types.ErrNoValidPrice(k.codespace)
} else if l == 1 {
// Return immediately if there's only one price
medianPrice = notExpiredPrices[0].Price
} else {
// sort the prices
sort.Slice(notExpiredPrices, func(i, j int) bool {
return notExpiredPrices[i].Price.LT(notExpiredPrices[j].Price)
})
// If there's an even number of prices
if l%2 == 0 {
// TODO make sure this is safe.
// Since it's a price and not a balance, division with precision loss is OK.
price1 := notExpiredPrices[l/2-1].Price
price2 := notExpiredPrices[l/2].Price
sum := price1.Add(price2)
divsor, _ := sdk.NewDecFromStr("2")
medianPrice = sum.Quo(divsor)
} else {
// integer division, so we'll get an integer back, rounded down
medianPrice = notExpiredPrices[l/2].Price
}
} }
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
currentPrice := types.CurrentPrice{ currentPrice := types.CurrentPrice{
AssetCode: assetCode, MarketID: marketID,
Price: medianPrice, Price: medianPrice,
} }
store.Set( store.Set(
[]byte(types.CurrentPricePrefix+assetCode), k.cdc.MustMarshalBinaryBare(currentPrice), []byte(types.CurrentPricePrefix+marketID), k.cdc.MustMarshalBinaryBare(currentPrice),
) )
return nil return nil
} }
// CalculateMedianPrice calculates the median prices for the input prices.
func (k Keeper) CalculateMedianPrice(ctx sdk.Context, prices []types.CurrentPrice) (sdk.Dec, sdk.Error) {
l := len(prices)
if l == 0 {
// Error if there are no valid prices in the raw pricefeed
return sdk.Dec{}, types.ErrNoValidPrice(k.codespace)
} else if l == 1 {
// Return immediately if there's only one price
return prices[0].Price, nil
} else {
// sort the prices
sort.Slice(prices, func(i, j int) bool {
return prices[i].Price.LT(prices[j].Price)
})
// for even numbers of prices, the median is calculated as the mean of the two middle prices
if l%2 == 0 {
median := k.calculateMeanPrice(ctx, prices[l/2-1:l/2+1])
return median, nil
}
// for odd numbers of prices, return the middle element
return prices[l/2].Price, nil
}
}
func (k Keeper) calculateMeanPrice(ctx sdk.Context, prices []types.CurrentPrice) sdk.Dec {
sum := prices[0].Price.Add(prices[1].Price)
mean := sum.Quo(sdk.NewDec(2))
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 asset
func (k Keeper) GetCurrentPrice(ctx sdk.Context, assetCode string) types.CurrentPrice { func (k Keeper) GetCurrentPrice(ctx sdk.Context, marketID string) types.CurrentPrice {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
bz := store.Get([]byte(types.CurrentPricePrefix + assetCode)) bz := store.Get([]byte(types.CurrentPricePrefix + marketID))
// TODO panic or return error if not found // TODO panic or return error if not found
var price types.CurrentPrice var price types.CurrentPrice
k.cdc.MustUnmarshalBinaryBare(bz, &price) k.cdc.MustUnmarshalBinaryBare(bz, &price)
@ -147,14 +154,15 @@ func (k Keeper) GetCurrentPrice(ctx sdk.Context, assetCode 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, assetCode string) []types.PostedPrice { func (k Keeper) GetRawPrices(ctx sdk.Context, marketID string) []types.PostedPrice {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
bz := store.Get([]byte(types.RawPriceFeedPrefix + assetCode)) 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)
return prices return prices
} }
// Codespace return the codespace for the keeper
func (k Keeper) Codespace() sdk.CodespaceType { func (k Keeper) Codespace() sdk.CodespaceType {
return k.codespace return k.codespace
} }

View File

@ -12,8 +12,8 @@ import (
"github.com/kava-labs/kava/x/pricefeed/types" "github.com/kava-labs/kava/x/pricefeed/types"
) )
// TestKeeper_SetGetAsset tests adding assets to the pricefeed, getting assets from the store // TestKeeper_SetGetMarket tests adding markets to the pricefeed, getting markets from the store
func TestKeeper_SetGetAsset(t *testing.T) { func TestKeeper_SetGetMarket(t *testing.T) {
helper := getMockApp(t, 0, types.GenesisState{}, nil) helper := getMockApp(t, 0, types.GenesisState{}, nil)
header := abci.Header{ header := abci.Header{
Height: helper.mApp.LastBlockHeight() + 1, Height: helper.mApp.LastBlockHeight() + 1,
@ -21,32 +21,32 @@ func TestKeeper_SetGetAsset(t *testing.T) {
helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := helper.mApp.BaseApp.NewContext(false, header) ctx := helper.mApp.BaseApp.NewContext(false, header)
ap := types.Params{ mp := types.Params{
Assets: []types.Asset{ Markets: types.Markets{
types.Asset{AssetCode: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true}, types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
}, },
} }
helper.keeper.SetParams(ctx, ap) helper.keeper.SetParams(ctx, mp)
assets := helper.keeper.GetAssetParams(ctx) markets := helper.keeper.GetMarketParams(ctx)
require.Equal(t, len(assets), 1) require.Equal(t, len(markets), 1)
require.Equal(t, assets[0].AssetCode, "tstusd") require.Equal(t, markets[0].MarketID, "tstusd")
_, found := helper.keeper.GetAsset(ctx, "tstusd") _, found := helper.keeper.GetMarket(ctx, "tstusd")
require.Equal(t, found, true) require.Equal(t, found, true)
ap = types.Params{ mp = types.Params{
Assets: []types.Asset{ Markets: types.Markets{
types.Asset{AssetCode: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true}, types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
types.Asset{AssetCode: "tst2usd", BaseAsset: "tst2", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true}, types.Market{MarketID: "tst2usd", BaseAsset: "tst2", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
}, },
} }
helper.keeper.SetParams(ctx, ap) helper.keeper.SetParams(ctx, mp)
assets = helper.keeper.GetAssetParams(ctx) markets = helper.keeper.GetMarketParams(ctx)
require.Equal(t, len(assets), 2) require.Equal(t, len(markets), 2)
require.Equal(t, assets[0].AssetCode, "tstusd") require.Equal(t, markets[0].MarketID, "tstusd")
require.Equal(t, assets[1].AssetCode, "tst2usd") require.Equal(t, markets[1].MarketID, "tst2usd")
_, found = helper.keeper.GetAsset(ctx, "nan") _, found = helper.keeper.GetMarket(ctx, "nan")
require.Equal(t, found, false) require.Equal(t, found, false)
} }
@ -58,12 +58,12 @@ func TestKeeper_GetSetPrice(t *testing.T) {
Time: tmtime.Now()} Time: tmtime.Now()}
helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := helper.mApp.BaseApp.NewContext(false, header) ctx := helper.mApp.BaseApp.NewContext(false, header)
ap := types.Params{ mp := types.Params{
Assets: []types.Asset{ Markets: types.Markets{
types.Asset{AssetCode: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true}, types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
}, },
} }
helper.keeper.SetParams(ctx, ap) helper.keeper.SetParams(ctx, mp)
// Set price by oracle 1 // Set price by oracle 1
_, err := helper.keeper.SetPrice( _, err := helper.keeper.SetPrice(
ctx, helper.addrs[0], "tstusd", ctx, helper.addrs[0], "tstusd",
@ -103,12 +103,12 @@ func TestKeeper_GetSetCurrentPrice(t *testing.T) {
Time: tmtime.Now()} Time: tmtime.Now()}
helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header}) helper.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := helper.mApp.BaseApp.NewContext(false, header) ctx := helper.mApp.BaseApp.NewContext(false, header)
ap := types.Params{ mp := types.Params{
Assets: []types.Asset{ Markets: types.Markets{
types.Asset{AssetCode: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true}, types.Market{MarketID: "tstusd", BaseAsset: "tst", QuoteAsset: "usd", Oracles: types.Oracles{}, Active: true},
}, },
} }
helper.keeper.SetParams(ctx, ap) helper.keeper.SetParams(ctx, mp)
helper.keeper.SetPrice( helper.keeper.SetPrice(
ctx, helper.addrs[0], "tstusd", ctx, helper.addrs[0], "tstusd",
sdk.MustNewDecFromStr("0.33"), sdk.MustNewDecFromStr("0.33"),

View File

@ -10,7 +10,7 @@ import (
// GetParams gets params from the store // GetParams gets 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.GetAssetParams(ctx)) return types.NewParams(k.GetMarketParams(ctx))
} }
// SetParams updates params in the store // SetParams updates params in the store
@ -18,47 +18,47 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramstore.SetParamSet(ctx, &params) k.paramstore.SetParamSet(ctx, &params)
} }
// GetAssetParams get asset params from store // GetMarketParams get asset params from store
func (k Keeper) GetAssetParams(ctx sdk.Context) types.Assets { func (k Keeper) GetMarketParams(ctx sdk.Context) types.Markets {
var assets types.Assets var markets types.Markets
k.paramstore.Get(ctx, types.KeyAssets, &assets) k.paramstore.Get(ctx, types.KeyMarkets, &markets)
return assets 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, assetCode string) (types.Oracles, error) { func (k Keeper) GetOracles(ctx sdk.Context, marketID string) (types.Oracles, error) {
for _, a := range k.GetAssetParams(ctx) { for _, m := range k.GetMarketParams(ctx) {
if assetCode == a.AssetCode { if marketID == m.MarketID {
return a.Oracles, nil return m.Oracles, nil
} }
} }
return types.Oracles{}, fmt.Errorf("asset %s not found", assetCode) return types.Oracles{}, fmt.Errorf("asset %s not found", 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, assetCode string, address sdk.AccAddress) (types.Oracle, error) { func (k Keeper) GetOracle(ctx sdk.Context, marketID string, address sdk.AccAddress) (types.Oracle, error) {
oracles, err := k.GetOracles(ctx, assetCode) oracles, err := k.GetOracles(ctx, marketID)
if err != nil { if err != nil {
return types.Oracle{}, fmt.Errorf("asset %s not found", assetCode) return types.Oracle{}, fmt.Errorf("asset %s not found", marketID)
} }
for _, o := range oracles { for _, o := range oracles {
if address.Equals(o.Address) { if address.Equals(o.Address) {
return o, nil return o, nil
} }
} }
return types.Oracle{}, fmt.Errorf("oracle %s not found for asset %s", address, assetCode) return types.Oracle{}, fmt.Errorf("oracle %s not found for asset %s", address, marketID)
} }
// GetAsset returns the asset if it is in the pricefeed system // GetMarket returns the market if it is in the pricefeed system
func (k Keeper) GetAsset(ctx sdk.Context, assetCode string) (types.Asset, bool) { func (k Keeper) GetMarket(ctx sdk.Context, marketID string) (types.Market, bool) {
assets := k.GetAssetParams(ctx) markets := k.GetMarketParams(ctx)
for i := range assets { for i := range markets {
if assets[i].AssetCode == assetCode { if markets[i].MarketID == marketID {
return assets[i], true return markets[i], true
} }
} }
return types.Asset{}, false return types.Market{}, false
} }

View File

@ -27,12 +27,12 @@ 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, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
assetCode := path[0] marketID := path[0]
_, found := keeper.GetAsset(ctx, assetCode) _, found := keeper.GetMarket(ctx, marketID)
if !found { if !found {
return []byte{}, sdk.ErrUnknownRequest("asset not found") return []byte{}, sdk.ErrUnknownRequest("asset not found")
} }
currentPrice := keeper.GetCurrentPrice(ctx, assetCode) currentPrice := keeper.GetCurrentPrice(ctx, marketID)
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, currentPrice) bz, err2 := codec.MarshalJSONIndent(keeper.cdc, currentPrice)
if err2 != nil { if err2 != nil {
@ -44,12 +44,12 @@ func queryCurrentPrice(ctx sdk.Context, path []string, req abci.RequestQuery, ke
func queryRawPrices(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { func queryRawPrices(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var priceList types.QueryRawPricesResp var priceList types.QueryRawPricesResp
assetCode := path[0] marketID := path[0]
_, found := keeper.GetAsset(ctx, assetCode) _, 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, assetCode) rawPrices := keeper.GetRawPrices(ctx, marketID)
for _, price := range rawPrices { for _, price := range rawPrices {
priceList = append(priceList, price.String()) priceList = append(priceList, price.String())
} }
@ -63,7 +63,7 @@ func queryRawPrices(ctx sdk.Context, path []string, req abci.RequestQuery, keepe
func queryAssets(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { func queryAssets(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var assetList types.QueryAssetsResp var assetList types.QueryAssetsResp
assets := keeper.GetAssetParams(ctx) assets := keeper.GetMarketParams(ctx)
for _, asset := range assets { for _, asset := range assets {
assetList = append(assetList, asset.String()) assetList = append(assetList, asset.String())
} }

View File

@ -8,34 +8,34 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// Asset struct that represents an asset in the pricefeed // Market struct that represents an asset in the pricefeed
type Asset struct { type Market struct {
AssetCode string `json:"asset_code" yaml:"asset_code"` 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 Oracles `json:"oracles" yaml:"oracles"`
Active bool `json:"active" yaml:"active"` Active bool `json:"active" yaml:"active"`
} }
// implement fmt.Stringer // String implement fmt.Stringer
func (a Asset) String() string { func (a Market) String() string {
return fmt.Sprintf(`Asset: return fmt.Sprintf(`Asset:
Asset Code: %s Market ID: %s
Base Asset: %s Base Asset: %s
Quote Asset: %s Quote Asset: %s
Oracles: %s Oracles: %s
Active: %t`, Active: %t`,
a.AssetCode, a.BaseAsset, a.QuoteAsset, a.Oracles, a.Active) a.MarketID, a.BaseAsset, a.QuoteAsset, a.Oracles, a.Active)
} }
// Assets array type for oracle // Markets array type for oracle
type Assets []Asset type Markets []Market
// String implements fmt.Stringer // String implements fmt.Stringer
func (as Assets) String() string { func (ms Markets) String() string {
out := "Assets:\n" out := "Markets:\n"
for _, a := range as { for _, m := range ms {
out += fmt.Sprintf("%s\n", a.String()) out += fmt.Sprintf("%s\n", m.String())
} }
return strings.TrimSpace(out) return strings.TrimSpace(out)
} }
@ -64,13 +64,13 @@ func (os Oracles) String() string {
// CurrentPrice struct that contains the metadata of a current price for a particular asset in the pricefeed module. // CurrentPrice struct that contains the metadata of a current price for a particular asset in the pricefeed module.
type CurrentPrice struct { type CurrentPrice struct {
AssetCode string `json:"asset_code" yaml:"asset_code"` 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 struct represented a price for an asset posted by a specific oracle
type PostedPrice struct { type PostedPrice struct {
AssetCode string `json:"asset_code" yaml:"asset_code"` 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"`
Price sdk.Dec `json:"price" yaml:"price"` Price sdk.Dec `json:"price" yaml:"price"`
Expiry time.Time `json:"expiry" yaml:"expiry"` Expiry time.Time `json:"expiry" yaml:"expiry"`
@ -78,16 +78,16 @@ type PostedPrice struct {
// implement fmt.Stringer // implement fmt.Stringer
func (cp CurrentPrice) String() string { func (cp CurrentPrice) String() string {
return strings.TrimSpace(fmt.Sprintf(`AssetCode: %s return strings.TrimSpace(fmt.Sprintf(`Market ID: %s
Price: %s`, cp.AssetCode, cp.Price)) Price: %s`, cp.MarketID, cp.Price))
} }
// implement fmt.Stringer // implement fmt.Stringer
func (pp PostedPrice) String() string { func (pp PostedPrice) String() string {
return strings.TrimSpace(fmt.Sprintf(`AssetCode: %s return strings.TrimSpace(fmt.Sprintf(`Market ID: %s
OracleAddress: %s Oracle Address: %s
Price: %s Price: %s
Expiry: %s`, pp.AssetCode, pp.OracleAddress, pp.Price, pp.Expiry)) Expiry: %s`, pp.MarketID, pp.OracleAddress, pp.Price, pp.Expiry))
} }
// SortDecs provides the interface needed to sort sdk.Dec slices // SortDecs provides the interface needed to sort sdk.Dec slices

View File

@ -0,0 +1,8 @@
package types
// Pricefeed module event types
const (
EventTypeNoValidPrices = "no_valid_prices"
AttributeKeyPriceUpdateFailed = "price_update_failed"
)

View File

@ -9,8 +9,8 @@ import (
) )
var ( var (
// KeyAssets store key for assets // KeyMarkets store key for markets
KeyAssets = []byte("assets") KeyMarkets = []byte("Markets")
) )
// ParamKeyTable Key declaration for parameters // ParamKeyTable Key declaration for parameters
@ -20,33 +20,33 @@ func ParamKeyTable() params.KeyTable {
// Params params for pricefeed. Can be altered via governance // Params params for pricefeed. Can be altered via governance
type Params struct { type Params struct {
Assets []Asset `json:"assets" yaml:"assets"` // Array containing the assets 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 // ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
// pairs of pricefeed module's parameters. // pairs of pricefeed module's parameters.
func (p Params) ParamSetPairs() params.ParamSetPairs { func (p Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{ return params.ParamSetPairs{
{Key: KeyAssets, Value: &p.Assets}, {Key: KeyMarkets, Value: &p.Markets},
} }
} }
// NewParams creates a new AssetParams object // NewParams creates a new AssetParams object
func NewParams(assets []Asset) Params { func NewParams(markets Markets) Params {
return Params{ return Params{
Assets: assets, Markets: markets,
} }
} }
// DefaultParams default params for pricefeed // DefaultParams default params for pricefeed
func DefaultParams() Params { func DefaultParams() Params {
return NewParams(Assets{}) return NewParams(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.Assets { for _, a := range p.Markets {
out += a.String() out += a.String()
} }
return strings.TrimSpace(out) return strings.TrimSpace(out)
@ -61,9 +61,9 @@ type ParamSubspace 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
for _, asset := range p.Assets { for _, asset := range p.Markets {
if asset.AssetCode == "" { if asset.MarketID == "" {
return fmt.Errorf("invalid asset: %s. missing asset code", asset.String()) return fmt.Errorf("invalid market: %s. missing market ID", asset.String())
} }
} }
return nil return nil