mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 08:15:19 +00:00
address review comments for pricefeed
This commit is contained in:
parent
1a9b8514c9
commit
bf83a9bf8f
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ const (
|
|||||||
CodeInvalidPrice = types.CodeInvalidPrice
|
CodeInvalidPrice = types.CodeInvalidPrice
|
||||||
CodeInvalidAsset = types.CodeInvalidAsset
|
CodeInvalidAsset = types.CodeInvalidAsset
|
||||||
CodeInvalidOracle = types.CodeInvalidOracle
|
CodeInvalidOracle = types.CodeInvalidOracle
|
||||||
|
EventTypeNoValidPrices = types.EventTypeNoValidPrices
|
||||||
|
AttributeKeyPriceUpdateFailed = types.AttributeKeyPriceUpdateFailed
|
||||||
ModuleName = types.ModuleName
|
ModuleName = types.ModuleName
|
||||||
StoreKey = types.StoreKey
|
StoreKey = types.StoreKey
|
||||||
RouterKey = types.RouterKey
|
RouterKey = types.RouterKey
|
||||||
@ -52,22 +54,22 @@ var (
|
|||||||
|
|
||||||
// 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
|
||||||
)
|
)
|
||||||
|
@ -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...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
|
@ -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, ¶ms)
|
k.paramstore.SetParamSet(ctx, ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
Oracle Address: %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
|
||||||
|
8
x/pricefeed/types/events.go
Normal file
8
x/pricefeed/types/events.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// Pricefeed module event types
|
||||||
|
const (
|
||||||
|
EventTypeNoValidPrices = "no_valid_prices"
|
||||||
|
|
||||||
|
AttributeKeyPriceUpdateFailed = "price_update_failed"
|
||||||
|
)
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user