Skip to content

Commit

Permalink
add timestamp to denom price
Browse files Browse the repository at this point in the history
  • Loading branch information
gsk967 committed Sep 19, 2023
1 parent bfd312a commit 929d49d
Show file tree
Hide file tree
Showing 20 changed files with 251 additions and 206 deletions.
8 changes: 5 additions & 3 deletions app/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,11 @@ func IntegrationTestNetworkConfig() network.Config {
// execute ballot voting and thus clear out previous exchange rates, since we
// are not running a price-feeder.
oracleGenState.Params.VotePeriod = 1000
oracleGenState.ExchangeRates = append(oracleGenState.ExchangeRates, oracletypes.NewExchangeRateTuple(
params.DisplayDenom, sdk.MustNewDecFromStr("34.21"),
))
oracleGenState.ExchangeRates = append(oracleGenState.ExchangeRates, oracletypes.ExchangeRate{
Denom: params.DisplayDenom,
Rate: sdk.MustNewDecFromStr("34.21"),
Timestamp: time.Now(),
})
// Set mock historic medians to satisfy leverage module's 24 median requirement
for i := 1; i <= 24; i++ {
median := oracletypes.Price{
Expand Down
23 changes: 8 additions & 15 deletions proto/umee/oracle/v1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,15 @@ option (gogoproto.goproto_getters_all) = false;

// GenesisState defines the oracle module's genesis state.
message GenesisState {
Params params = 1 [(gogoproto.nullable) = false];
repeated FeederDelegation feeder_delegations = 2
[(gogoproto.nullable) = false];
// TODO: need to update this to save data with timestamp
repeated ExchangeRateTuple exchange_rates = 3 [
(gogoproto.castrepeated) = "ExchangeRateTuples",
(gogoproto.nullable) = false
];
Params params = 1 [(gogoproto.nullable) = false];
repeated FeederDelegation feeder_delegations = 2 [(gogoproto.nullable) = false];
repeated ExchangeRate exchange_rates = 3 [(gogoproto.nullable) = false];
repeated MissCounter miss_counters = 4 [(gogoproto.nullable) = false];
repeated AggregateExchangeRatePrevote aggregate_exchange_rate_prevotes = 5
[(gogoproto.nullable) = false];
repeated AggregateExchangeRateVote aggregate_exchange_rate_votes = 6
[(gogoproto.nullable) = false];
repeated Price medians = 7 [(gogoproto.nullable) = false];
repeated Price historic_prices = 8 [(gogoproto.nullable) = false];
repeated Price medianDeviations = 9 [(gogoproto.nullable) = false];
repeated AggregateExchangeRatePrevote aggregate_exchange_rate_prevotes = 5 [(gogoproto.nullable) = false];
repeated AggregateExchangeRateVote aggregate_exchange_rate_votes = 6 [(gogoproto.nullable) = false];
repeated Price medians = 7 [(gogoproto.nullable) = false];
repeated Price historic_prices = 8 [(gogoproto.nullable) = false];
repeated Price medianDeviations = 9 [(gogoproto.nullable) = false];
// Historic Avg Counter params
AvgCounterParams avg_counter_params = 10 [
(gogoproto.moretags) = "yaml:\"avg_counter_params\"",
Expand Down
4 changes: 2 additions & 2 deletions proto/umee/oracle/v1/oracle.proto
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ message AvgCounter {
message ExchangeRate {
option (gogoproto.equal) = false;
option (gogoproto.goproto_stringer) = false;

string rate = 1 [
string denom = 1 [(gogoproto.moretags) = "yaml:\"denom\""];
string rate = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
Expand Down
2 changes: 1 addition & 1 deletion x/leverage/simulation/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (s *SimTestSuite) SetupTest() {

// Use default umee token for sim tests
s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, fixtures.Token("uumee", "UMEE", 6)))
app.OracleKeeper.SetExchangeRate(ctx, "UMEE", sdk.MustNewDecFromStr("100.0"))
app.OracleKeeper.SetExchangeRate(ctx, "UMEE", sdk.MustNewDecFromStr("100.0"), s.ctx.BlockTime())
for i := 1; i <= 24; i++ {
// set historic medians for UMEE on blocks 1-24 (without actually advancing block height)
// this is to accommodate leverage module's default 24 historic median requirement
Expand Down
3 changes: 2 additions & 1 deletion x/oracle/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func CalcPrices(ctx sdk.Context, params types.Params, k keeper.Keeper) error {
if err != nil {
return err
}
k.SetExchangeRateWithEvent(ctx, denom, exchangeRate)
// save the exchange rate to store with denom and timestamp
k.SetExchangeRateWithEvent(ctx, denom, exchangeRate, ctx.BlockTime())

if k.IsPeriodLastBlock(ctx, params.HistoricStampPeriod) {
k.AddHistoricPrice(ctx, denom, exchangeRate)
Expand Down
14 changes: 9 additions & 5 deletions x/oracle/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ func (s *IntegrationTestSuite) TestEndBlockerVoteThreshold() {
for _, denom := range app.OracleKeeper.AcceptList(ctx) {
rate, err := app.OracleKeeper.GetExchangeRate(ctx, denom.SymbolDenom)
s.Require().NoError(err)
s.Require().Equal(types.ExchangeRate{Rate: sdk.OneDec(), Timestamp: ctx.BlockTime()},
s.Require().Equal(types.ExchangeRate{
Rate: sdk.OneDec(),
Denom: denom.SymbolDenom,
Timestamp: ctx.BlockTime()},
rate)
}

Expand Down Expand Up @@ -199,7 +202,8 @@ func (s *IntegrationTestSuite) TestEndBlockerVoteThreshold() {
for _, denom := range app.OracleKeeper.AcceptList(ctx) {
rate, err := app.OracleKeeper.GetExchangeRate(ctx, denom.SymbolDenom)
s.Require().NoError(err)
s.Require().Equal(types.ExchangeRate{Rate: sdk.NewDecWithPrec(5, 1), Timestamp: ctx.BlockTime()}, rate)
s.Require().Equal(types.ExchangeRate{
Denom: denom.SymbolDenom, Rate: sdk.NewDecWithPrec(5, 1), Timestamp: ctx.BlockTime()}, rate)
}

// TODO: check reward distribution
Expand Down Expand Up @@ -236,9 +240,9 @@ func (s *IntegrationTestSuite) TestEndBlockerVoteThreshold() {

rate, err := app.OracleKeeper.GetExchangeRate(ctx, "umee")
s.Require().NoError(err)
s.Require().Equal(types.ExchangeRate{Rate: sdk.OneDec(), Timestamp: ctx.BlockTime()}, rate)
rate, err = app.OracleKeeper.GetExchangeRate(ctx, "atom")
s.Require().ErrorIs(err, types.ErrUnknownDenom.Wrap("atom"))
s.Require().Equal(types.ExchangeRate{Denom: "UMEE", Rate: sdk.OneDec(), Timestamp: ctx.BlockTime()}, rate)
rate, err = app.OracleKeeper.GetExchangeRate(ctx, "ATOM")
s.Require().ErrorIs(err, types.ErrUnknownDenom.Wrap("ATOM"))
s.Require().Equal(types.ExchangeRate{}, rate)
}

Expand Down
12 changes: 4 additions & 8 deletions x/oracle/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, genState types.GenesisSt
}

for _, ex := range genState.ExchangeRates {
keeper.SetExchangeRate(ctx, ex.Denom, ex.ExchangeRate)
keeper.SetExchangeRate(ctx, ex.Denom, ex.Rate, ex.Timestamp)
}

for _, mc := range genState.MissCounters {
Expand Down Expand Up @@ -91,13 +91,9 @@ func ExportGenesis(ctx sdk.Context, keeper keeper.Keeper) *types.GenesisState {
return false
})

exchangeRates := []types.ExchangeRateTuple{}
keeper.IterateExchangeRates(ctx, func(denom string, rate sdk.Dec) (stop bool) {
exchangeRates = append(exchangeRates, types.ExchangeRateTuple{
Denom: denom,
ExchangeRate: rate,
})

exchangeRates := []types.ExchangeRate{}
keeper.IterateExchangeRates(ctx, func(er types.ExchangeRate) (stop bool) {
exchangeRates = append(exchangeRates, er)
return false
})

Expand Down
18 changes: 10 additions & 8 deletions x/oracle/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ func (s *IntegrationTestSuite) TestGenesis_InitGenesis() {
"valid",
types.GenesisState{
Params: types.DefaultParams(),
ExchangeRates: types.ExchangeRateTuples{
types.ExchangeRateTuple{
Denom: denom,
ExchangeRate: exchangeRate,
ExchangeRates: []types.ExchangeRate{
{
Denom: denom,
Rate: exchangeRate,
Timestamp: ctx.BlockTime(),
},
},
HistoricPrices: types.Prices{
Expand Down Expand Up @@ -152,10 +153,11 @@ func (s *IntegrationTestSuite) TestGenesis_ExportGenesis() {
ValidatorAddress: umeevaloperAddr,
},
}
exchangeRateTuples := types.ExchangeRateTuples{
types.ExchangeRateTuple{
Denom: upperDenom,
ExchangeRate: exchangeRate,
exchangeRateTuples := []types.ExchangeRate{
{
Denom: upperDenom,
Rate: exchangeRate,
Timestamp: ctx.BlockTime(),
},
}
missCounters := []types.MissCounter{
Expand Down
2 changes: 1 addition & 1 deletion x/oracle/keeper/ballot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

func (s *IntegrationTestSuite) TestBallot_OrganizeBallotByDenom() {
require := s.Require()
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec())
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec(), s.ctx.BlockTime())
claimMap := make(map[string]types.Claim)

// Empty Map
Expand Down
8 changes: 4 additions & 4 deletions x/oracle/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ func (q querier) ExchangeRates(

exchangeRates = exchangeRates.Add(sdk.NewDecCoinFromDec(req.Denom, exchangeRate.Rate))
} else {
q.IterateExchangeRates(ctx, func(denom string, rate sdk.Dec) (stop bool) {
exchangeRates = exchangeRates.Add(sdk.NewDecCoinFromDec(denom, rate))
q.IterateExchangeRates(ctx, func(exgRate types.ExchangeRate) (stop bool) {
exchangeRates = exchangeRates.Add(sdk.NewDecCoinFromDec(exgRate.Denom, exgRate.Rate))
return false
})
}
Expand All @@ -85,8 +85,8 @@ func (q querier) ActiveExchangeRates(
ctx := sdk.UnwrapSDKContext(goCtx)

denoms := []string{}
q.IterateExchangeRates(ctx, func(denom string, _ sdk.Dec) (stop bool) {
denoms = append(denoms, denom)
q.IterateExchangeRates(ctx, func(exgRate types.ExchangeRate) (stop bool) {
denoms = append(denoms, exgRate.Denom)
return false
})

Expand Down
11 changes: 8 additions & 3 deletions x/oracle/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import (
)

func (s *IntegrationTestSuite) TestQuerier_ActiveExchangeRates() {
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec())
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec(), s.ctx.BlockTime())
res, err := s.queryClient.ActiveExchangeRates(s.ctx.Context(), &types.QueryActiveExchangeRates{})
s.Require().NoError(err)
s.Require().Equal([]string{displayDenom}, res.ActiveRates)
}

func (s *IntegrationTestSuite) TestQuerier_ExchangeRates() {
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec())
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec(), s.ctx.BlockTime())
res, err := s.queryClient.ExchangeRates(s.ctx.Context(), &types.QueryExchangeRates{})
s.Require().NoError(err)
s.Require().Equal(sdk.DecCoins{
Expand Down Expand Up @@ -193,8 +193,13 @@ func (s *IntegrationTestSuite) TestQuerier_AggregatePrevotesAppendVotes() {
}

func (s *IntegrationTestSuite) TestQuerier_AggregateVotesAppendVotes() {
exgRateTuples := types.ExchangeRateTuples{}

for _, er := range types.DefaultGenesisState().ExchangeRates {
exgRateTuples = append(exgRateTuples, types.ExchangeRateTuple{Denom: er.Denom, ExchangeRate: er.Rate})
}
s.app.OracleKeeper.SetAggregateExchangeRateVote(s.ctx, valAddr, types.NewAggregateExchangeRateVote(
types.DefaultGenesisState().ExchangeRates,
exgRateTuples,
valAddr,
))

Expand Down
22 changes: 10 additions & 12 deletions x/oracle/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"fmt"
"time"

"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
Expand Down Expand Up @@ -111,38 +112,35 @@ func (k Keeper) GetExchangeRateBase(ctx sdk.Context, denom string) (sdk.Dec, err
}

// SetExchangeRate sets the consensus exchange rate of USD denominated in the
// denom asset to the store.
func (k Keeper) SetExchangeRate(ctx sdk.Context, denom string, rate sdk.Dec) {
// denom asset to the store with timestamp
func (k Keeper) SetExchangeRate(ctx sdk.Context, denom string, rate sdk.Dec, t time.Time) {
key := types.KeyExchangeRate(denom)
val := types.ExchangeRate{Rate: rate, Timestamp: ctx.BlockTime()}
err := store.SetValue(ctx.KVStore(k.storeKey), key, &val, "exchange_rate")
val := types.ExchangeRate{Denom: denom, Rate: rate, Timestamp: t}
err := store.SetValue[*types.ExchangeRate](ctx.KVStore(k.storeKey), key, &val, "exchange_rate")
util.Panic(err)
}

// SetExchangeRateWithEvent sets an consensus
// exchange rate to the store with ABCI event
func (k Keeper) SetExchangeRateWithEvent(ctx sdk.Context, denom string, exchangeRate sdk.Dec) {
k.SetExchangeRate(ctx, denom, exchangeRate)
func (k Keeper) SetExchangeRateWithEvent(ctx sdk.Context, denom string, rate sdk.Dec, t time.Time) {
k.SetExchangeRate(ctx, denom, rate, t)
sdkutil.Emit(&ctx, &types.EventSetFxRate{
Denom: denom, Rate: exchangeRate,
Denom: denom, Rate: rate,
})
}

// IterateExchangeRates iterates over all USD rates in the store.
// TODO: handler should use ExchangeRate type rather than Dec
func (k Keeper) IterateExchangeRates(ctx sdk.Context, handler func(string, sdk.Dec) bool) {
func (k Keeper) IterateExchangeRates(ctx sdk.Context, handler func(types.ExchangeRate) bool) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, types.KeyPrefixExchangeRate)
defer iter.Close()
prefixLen := len(types.KeyPrefixExchangeRate)

for ; iter.Valid(); iter.Next() {
key := iter.Key()
denom := string(key[prefixLen : len(key)-1]) // -1 to remove the null suffix
var er types.ExchangeRate
k.cdc.MustUnmarshal(iter.Value(), &er)

if handler(denom, er.Rate) {
if handler(er) {
break
}
}
Expand Down
16 changes: 8 additions & 8 deletions x/oracle/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,10 @@ func (s *IntegrationTestSuite) TestAggregateExchangeRateVoteError() {

func (s *IntegrationTestSuite) TestSetExchangeRateWithEvent() {
v := sdk.OneDec()
s.app.OracleKeeper.SetExchangeRateWithEvent(s.ctx, displayDenom, v)
s.app.OracleKeeper.SetExchangeRateWithEvent(s.ctx, displayDenom, v, s.ctx.BlockTime())
rate, err := s.app.OracleKeeper.GetExchangeRate(s.ctx, displayDenom)
s.Require().NoError(err)
s.Require().Equal(rate, types.ExchangeRate{Rate: v, Timestamp: s.ctx.BlockTime()})
s.Require().Equal(rate, types.ExchangeRate{Rate: v, Timestamp: s.ctx.BlockTime(), Denom: displayDenom})
}

func (s *IntegrationTestSuite) TestGetExchangeRate_InvalidDenom() {
Expand All @@ -228,13 +228,13 @@ func (s *IntegrationTestSuite) TestGetExchangeRate_NotSet() {

func (s *IntegrationTestSuite) TestGetExchangeRate_Valid() {
v := sdk.OneDec()
expected := types.ExchangeRate{Rate: v, Timestamp: s.ctx.BlockTime()}
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, v)
expected := types.ExchangeRate{Rate: v, Timestamp: s.ctx.BlockTime(), Denom: displayDenom}
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, v, s.ctx.BlockTime())
rate, err := s.app.OracleKeeper.GetExchangeRate(s.ctx, displayDenom)
s.Require().NoError(err)
s.Require().Equal(rate, expected)

s.app.OracleKeeper.SetExchangeRate(s.ctx, strings.ToLower(displayDenom), sdk.OneDec())
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec(), s.ctx.BlockTime())
rate, err = s.app.OracleKeeper.GetExchangeRate(s.ctx, displayDenom)
s.Require().NoError(err)
s.Require().Equal(rate, expected)
Expand All @@ -252,12 +252,12 @@ func (s *IntegrationTestSuite) TestGetExchangeRateBase() {

power := sdk.MustNewDecFromStr("10").Power(exponent)

s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec())
s.app.OracleKeeper.SetExchangeRate(s.ctx, displayDenom, sdk.OneDec(), s.ctx.BlockTime())
rate, err := s.app.OracleKeeper.GetExchangeRateBase(s.ctx, bondDenom)
s.Require().NoError(err)
s.Require().Equal(rate.Mul(power), sdk.OneDec())

s.app.OracleKeeper.SetExchangeRate(s.ctx, strings.ToLower(displayDenom), sdk.OneDec())
s.app.OracleKeeper.SetExchangeRate(s.ctx, strings.ToLower(displayDenom), sdk.OneDec(), s.ctx.BlockTime())
rate, err = s.app.OracleKeeper.GetExchangeRateBase(s.ctx, bondDenom)
s.Require().NoError(err)
s.Require().Equal(rate.Mul(power), sdk.OneDec())
Expand All @@ -266,7 +266,7 @@ func (s *IntegrationTestSuite) TestGetExchangeRateBase() {
func (s *IntegrationTestSuite) TestClearExchangeRate() {
app, ctx := s.app, s.ctx

app.OracleKeeper.SetExchangeRate(ctx, displayDenom, sdk.OneDec())
app.OracleKeeper.SetExchangeRate(ctx, displayDenom, sdk.OneDec(), s.ctx.BlockTime())
app.OracleKeeper.ClearExchangeRates(ctx)
_, err := app.OracleKeeper.GetExchangeRate(ctx, displayDenom)
s.Require().Error(err)
Expand Down
7 changes: 4 additions & 3 deletions x/oracle/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (s *IntegrationTestSuite) TestMsgServer_AggregateExchangeRateVote() {
acceptList := s.app.OracleKeeper.GetParams(ctx).AcceptList
var acceptListFlat []string
for _, v := range acceptList {
acceptListFlat = append(acceptListFlat, v.SymbolDenom)
acceptListFlat = append(acceptListFlat, strings.ToUpper(v.SymbolDenom))
}

// No existing prevote
Expand All @@ -122,7 +122,7 @@ func (s *IntegrationTestSuite) TestMsgServer_AggregateExchangeRateVote() {
vote, err := s.app.OracleKeeper.GetAggregateExchangeRateVote(ctx, valAddr)
s.Require().Nil(err)
for _, v := range vote.ExchangeRateTuples {
s.Require().Contains(acceptListFlat, strings.ToLower(v.Denom))
s.Require().Contains(acceptListFlat, v.Denom)
}

// Valid, but with an exchange rate which isn't in AcceptList
Expand All @@ -132,12 +132,13 @@ func (s *IntegrationTestSuite) TestMsgServer_AggregateExchangeRateVote() {
types.NewAggregateExchangeRatePrevote(
hashInvalidRate, valAddr, 1,
))

_, err = s.msgServer.AggregateExchangeRateVote(sdk.WrapSDKContext(ctx), voteMsgInvalidRate)
s.Require().NoError(err)
vote, err = s.app.OracleKeeper.GetAggregateExchangeRateVote(ctx, valAddr)
s.Require().NoError(err)
for _, v := range vote.ExchangeRateTuples {
s.Require().Contains(acceptListFlat, strings.ToLower(v.Denom))
s.Require().Contains(acceptListFlat, v.Denom)
}
}

Expand Down
Loading

0 comments on commit 929d49d

Please sign in to comment.