From ce2d90d05d729e8d57a0799494ba1acf3435bfba Mon Sep 17 00:00:00 2001 From: rbajollari Date: Tue, 13 Dec 2022 19:46:26 -0500 Subject: [PATCH 1/6] Add AvailableMedians method --- x/oracle/keeper/historic_price.go | 9 +++++++++ x/oracle/keeper/historic_price_test.go | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/x/oracle/keeper/historic_price.go b/x/oracle/keeper/historic_price.go index 9e466c89fc..8ebadd620d 100644 --- a/x/oracle/keeper/historic_price.go +++ b/x/oracle/keeper/historic_price.go @@ -27,6 +27,15 @@ func (k Keeper) HistoricMedians( return medians } +func (k Keeper) AvailableMedians( + ctx sdk.Context, + denom string, +) uint32 { + medians := k.HistoricMedians(ctx, denom, k.MaximumMedianStamps(ctx)) + + return uint32(len(medians)) +} + // CalcAndSetHistoricMedian uses all the historic prices of a given denom to // calculate its median price at the current block and set it to the store. // It will also call setMedianDeviation with the calculated median. diff --git a/x/oracle/keeper/historic_price_test.go b/x/oracle/keeper/historic_price_test.go index f0237c4911..cc47e4418d 100644 --- a/x/oracle/keeper/historic_price_test.go +++ b/x/oracle/keeper/historic_price_test.go @@ -32,13 +32,16 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) } - // check median and median standard deviation + // check medians, num of available medians, and median standard deviation medians := app.OracleKeeper.HistoricMedians(ctx, displayDenom, 3) s.Require().Equal(len(medians), 3) s.Require().Equal(medians[0], sdk.MustNewDecFromStr("1.2")) s.Require().Equal(medians[1], sdk.MustNewDecFromStr("1.125")) s.Require().Equal(medians[2], sdk.MustNewDecFromStr("1.1")) + availableMedians := app.OracleKeeper.AvailableMedians(ctx, displayDenom) + s.Require().Equal(availableMedians, uint32(3)) + medianDeviation, err := app.OracleKeeper.HistoricMedianDeviation(ctx, displayDenom) s.Require().NoError(err) s.Require().Equal(medianDeviation, sdk.MustNewDecFromStr("0.012499999999999998")) @@ -82,6 +85,9 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { firstStampBlock := lastStampBlock - blockPeriod app.OracleKeeper.DeleteHistoricMedian(ctx, displayDenom, firstStampBlock) + availableMedians = app.OracleKeeper.AvailableMedians(ctx, displayDenom) + s.Require().Equal(availableMedians, uint32(2)) + medians = app.OracleKeeper.HistoricMedians(ctx, displayDenom, 3) s.Require().Equal(len(medians), 2) s.Require().Equal(medians[0], sdk.MustNewDecFromStr("1.2")) From 41414ae08289ad6c9f99d96e7a984a35ed594b45 Mon Sep 17 00:00:00 2001 From: rbajollari Date: Tue, 13 Dec 2022 19:59:01 -0500 Subject: [PATCH 2/6] Add 0 medians scenario to test --- x/oracle/keeper/historic_price_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x/oracle/keeper/historic_price_test.go b/x/oracle/keeper/historic_price_test.go index cc47e4418d..aedba1d33d 100644 --- a/x/oracle/keeper/historic_price_test.go +++ b/x/oracle/keeper/historic_price_test.go @@ -16,6 +16,9 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { // similar prefixes displayDenomVariation := displayDenom + "test" + availableMedians := app.OracleKeeper.AvailableMedians(ctx, displayDenom) + s.Require().Equal(availableMedians, uint32(0)) + // add multiple historic prices to store exchangeRates := []string{"1.0", "1.2", "1.1", "1.4", "1.1", "1.15", "1.2", "1.3", "1.2"} for i, exchangeRate := range exchangeRates { @@ -39,7 +42,7 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { s.Require().Equal(medians[1], sdk.MustNewDecFromStr("1.125")) s.Require().Equal(medians[2], sdk.MustNewDecFromStr("1.1")) - availableMedians := app.OracleKeeper.AvailableMedians(ctx, displayDenom) + availableMedians = app.OracleKeeper.AvailableMedians(ctx, displayDenom) s.Require().Equal(availableMedians, uint32(3)) medianDeviation, err := app.OracleKeeper.HistoricMedianDeviation(ctx, displayDenom) From 9ecaa17e8dce4c885c3aad707090af393ab48124 Mon Sep 17 00:00:00 2001 From: rbajollari Date: Wed, 14 Dec 2022 09:06:20 -0500 Subject: [PATCH 3/6] AvailableMedians godoc --- x/oracle/keeper/historic_price.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/oracle/keeper/historic_price.go b/x/oracle/keeper/historic_price.go index 8ebadd620d..064ad03e44 100644 --- a/x/oracle/keeper/historic_price.go +++ b/x/oracle/keeper/historic_price.go @@ -27,6 +27,8 @@ func (k Keeper) HistoricMedians( return medians } +// AvailableMedians returns the number of medians in the store for a given denom +// when called. func (k Keeper) AvailableMedians( ctx sdk.Context, denom string, From b8306821f1112160402af81ea1933e2ae6daeffe Mon Sep 17 00:00:00 2001 From: rbajollari Date: Wed, 14 Dec 2022 13:44:22 -0500 Subject: [PATCH 4/6] Add nummedians return to median stat methods --- x/oracle/abci_test.go | 12 ++++++---- x/oracle/keeper/historic_price.go | 32 +++++++++++++------------- x/oracle/keeper/historic_price_test.go | 24 ++++++++++++------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/x/oracle/abci_test.go b/x/oracle/abci_test.go index 8a3b4e0428..91b66da5da 100644 --- a/x/oracle/abci_test.go +++ b/x/oracle/abci_test.go @@ -265,16 +265,20 @@ func (s *IntegrationTestSuite) TestEndblockerHistoracle() { s.Require().NoError(err) s.Require().Equal(tc.expectedWithinHistoricMedianDeviation, withinHistoricMedianDeviation) - medianOfHistoricMedians, err := app.OracleKeeper.MedianOfHistoricMedians(ctx, denom.SymbolDenom, 6) + medianOfHistoricMedians, numMedians, err := app.OracleKeeper.MedianOfHistoricMedians(ctx, denom.SymbolDenom, 6) + s.Require().Equal(uint32(4), numMedians) s.Require().Equal(tc.expectedMedianOfHistoricMedians, medianOfHistoricMedians) - averageOfHistoricMedians, err := app.OracleKeeper.AverageOfHistoricMedians(ctx, denom.SymbolDenom, 6) + averageOfHistoricMedians, numMedians, err := app.OracleKeeper.AverageOfHistoricMedians(ctx, denom.SymbolDenom, 6) + s.Require().Equal(uint32(4), numMedians) s.Require().Equal(tc.expectedAverageOfHistoricMedians, averageOfHistoricMedians) - minOfHistoricMedians, err := app.OracleKeeper.MinOfHistoricMedians(ctx, denom.SymbolDenom, 6) + minOfHistoricMedians, numMedians, err := app.OracleKeeper.MinOfHistoricMedians(ctx, denom.SymbolDenom, 6) + s.Require().Equal(uint32(4), numMedians) s.Require().Equal(tc.expectedMinOfHistoricMedians, minOfHistoricMedians) - maxOfHistoricMedians, err := app.OracleKeeper.MaxOfHistoricMedians(ctx, denom.SymbolDenom, 6) + maxOfHistoricMedians, numMedians, err := app.OracleKeeper.MaxOfHistoricMedians(ctx, denom.SymbolDenom, 6) + s.Require().Equal(uint32(4), numMedians) s.Require().Equal(tc.expectedMaxOfHistoricMedians, maxOfHistoricMedians) clearHistoricPrices(ctx, app.OracleKeeper, denom.SymbolDenom) diff --git a/x/oracle/keeper/historic_price.go b/x/oracle/keeper/historic_price.go index 064ad03e44..d8d1e0a3ee 100644 --- a/x/oracle/keeper/historic_price.go +++ b/x/oracle/keeper/historic_price.go @@ -145,63 +145,63 @@ func (k Keeper) SetHistoricMedianDeviation( } // MedianOfHistoricMedians calculates and returns the median of the last stampNum -// historic medians. +// historic medians as well as the amount of medians used to calculate that median. func (k Keeper) MedianOfHistoricMedians( ctx sdk.Context, denom string, numStamps uint64, -) (sdk.Dec, error) { +) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) median, err := decmath.Median(medians) if err != nil { - return sdk.ZeroDec(), sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) + return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } - return median, nil + return median, uint32(len(medians)), nil } // AverageOfHistoricMedians calculates and returns the average of the last stampNum -// historic medians. +// historic medians as well as the amount of medians used to calculate that average. func (k Keeper) AverageOfHistoricMedians( ctx sdk.Context, denom string, numStamps uint64, -) (sdk.Dec, error) { +) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) average, err := decmath.Average(medians) if err != nil { - return sdk.ZeroDec(), sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) + return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } - return average, nil + return average, uint32(len(medians)), nil } // MaxOfHistoricMedian calculates and returns the maximum value of the last stampNum -// historic medians. +// historic medians as well as the amount of medians used to calculate that maximum. func (k Keeper) MaxOfHistoricMedians( ctx sdk.Context, denom string, numStamps uint64, -) (sdk.Dec, error) { +) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) max, err := decmath.Max(medians) if err != nil { - return sdk.ZeroDec(), sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) + return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } - return max, nil + return max, uint32(len(medians)), nil } // MinOfHistoricMedians calculates and returns the minimum value of the last stampNum -// historic medians. +// historic medians as well as the amount of medians used to calculate that minimum. func (k Keeper) MinOfHistoricMedians( ctx sdk.Context, denom string, numStamps uint64, -) (sdk.Dec, error) { +) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) min, err := decmath.Min(medians) if err != nil { - return sdk.ZeroDec(), sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) + return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } - return min, nil + return min, uint32(len(medians)), nil } // historicPrices returns all the historic prices of a given denom. diff --git a/x/oracle/keeper/historic_price_test.go b/x/oracle/keeper/historic_price_test.go index aedba1d33d..4fdd59ce27 100644 --- a/x/oracle/keeper/historic_price_test.go +++ b/x/oracle/keeper/historic_price_test.go @@ -55,31 +55,39 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { s.Require().NoError(err) // check median stats of last 3 stamps - medianOfMedians, err := app.OracleKeeper.MedianOfHistoricMedians(ctx, displayDenom, 3) + medianOfMedians, numMedians, err := app.OracleKeeper.MedianOfHistoricMedians(ctx, displayDenom, 3) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(3)) s.Require().Equal(medianOfMedians, sdk.MustNewDecFromStr("1.125")) - averageOfMedians, err := app.OracleKeeper.AverageOfHistoricMedians(ctx, displayDenom, 3) + averageOfMedians, numMedians, err := app.OracleKeeper.AverageOfHistoricMedians(ctx, displayDenom, 3) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(3)) s.Require().Equal(averageOfMedians, sdk.MustNewDecFromStr("1.141666666666666666")) - maxMedian, err := app.OracleKeeper.MaxOfHistoricMedians(ctx, displayDenom, 3) + maxMedian, numMedians, err := app.OracleKeeper.MaxOfHistoricMedians(ctx, displayDenom, 3) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(3)) s.Require().Equal(maxMedian, sdk.MustNewDecFromStr("1.2")) - minMedian, err := app.OracleKeeper.MinOfHistoricMedians(ctx, displayDenom, 3) + minMedian, numMedians, err := app.OracleKeeper.MinOfHistoricMedians(ctx, displayDenom, 3) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(3)) s.Require().Equal(minMedian, sdk.MustNewDecFromStr("1.1")) // check median stats of last 1 stamps - medianOfMedians, err = app.OracleKeeper.MedianOfHistoricMedians(ctx, displayDenom, 1) + medianOfMedians, numMedians, err = app.OracleKeeper.MedianOfHistoricMedians(ctx, displayDenom, 1) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(1)) s.Require().Equal(medianOfMedians, sdk.MustNewDecFromStr("1.2")) - averageOfMedians, err = app.OracleKeeper.AverageOfHistoricMedians(ctx, displayDenom, 1) + averageOfMedians, numMedians, err = app.OracleKeeper.AverageOfHistoricMedians(ctx, displayDenom, 1) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(1)) s.Require().Equal(averageOfMedians, sdk.MustNewDecFromStr("1.2")) - maxMedian, err = app.OracleKeeper.MaxOfHistoricMedians(ctx, displayDenom, 1) + maxMedian, numMedians, err = app.OracleKeeper.MaxOfHistoricMedians(ctx, displayDenom, 1) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(1)) s.Require().Equal(maxMedian, sdk.MustNewDecFromStr("1.2")) - minMedian, err = app.OracleKeeper.MinOfHistoricMedians(ctx, displayDenom, 1) + minMedian, numMedians, err = app.OracleKeeper.MinOfHistoricMedians(ctx, displayDenom, 1) s.Require().NoError(err) + s.Require().Equal(numMedians, uint32(1)) s.Require().Equal(minMedian, sdk.MustNewDecFromStr("1.2")) // delete first median From c670916bcbcad41708edafda3e63c371c4368159 Mon Sep 17 00:00:00 2001 From: rbajollari Date: Thu, 15 Dec 2022 11:54:58 -0500 Subject: [PATCH 5/6] Remove AvailableMedians --- x/oracle/keeper/historic_price.go | 27 +++++++++++++++----------- x/oracle/keeper/historic_price_test.go | 9 --------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/x/oracle/keeper/historic_price.go b/x/oracle/keeper/historic_price.go index d8d1e0a3ee..e1f15cf0a5 100644 --- a/x/oracle/keeper/historic_price.go +++ b/x/oracle/keeper/historic_price.go @@ -27,17 +27,6 @@ func (k Keeper) HistoricMedians( return medians } -// AvailableMedians returns the number of medians in the store for a given denom -// when called. -func (k Keeper) AvailableMedians( - ctx sdk.Context, - denom string, -) uint32 { - medians := k.HistoricMedians(ctx, denom, k.MaximumMedianStamps(ctx)) - - return uint32(len(medians)) -} - // CalcAndSetHistoricMedian uses all the historic prices of a given denom to // calculate its median price at the current block and set it to the store. // It will also call setMedianDeviation with the calculated median. @@ -152,10 +141,14 @@ func (k Keeper) MedianOfHistoricMedians( numStamps uint64, ) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) + if len(medians) == 0 { + return sdk.ZeroDec(), 0, nil + } median, err := decmath.Median(medians) if err != nil { return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } + return median, uint32(len(medians)), nil } @@ -167,10 +160,14 @@ func (k Keeper) AverageOfHistoricMedians( numStamps uint64, ) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) + if len(medians) == 0 { + return sdk.ZeroDec(), 0, nil + } average, err := decmath.Average(medians) if err != nil { return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } + return average, uint32(len(medians)), nil } @@ -182,10 +179,14 @@ func (k Keeper) MaxOfHistoricMedians( numStamps uint64, ) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) + if len(medians) == 0 { + return sdk.ZeroDec(), 0, nil + } max, err := decmath.Max(medians) if err != nil { return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } + return max, uint32(len(medians)), nil } @@ -197,10 +198,14 @@ func (k Keeper) MinOfHistoricMedians( numStamps uint64, ) (sdk.Dec, uint32, error) { medians := k.HistoricMedians(ctx, denom, numStamps) + if len(medians) == 0 { + return sdk.ZeroDec(), 0, nil + } min, err := decmath.Min(medians) if err != nil { return sdk.ZeroDec(), 0, sdkerrors.Wrap(err, fmt.Sprintf("denom: %s", denom)) } + return min, uint32(len(medians)), nil } diff --git a/x/oracle/keeper/historic_price_test.go b/x/oracle/keeper/historic_price_test.go index 8f7f579343..c109dfac1e 100644 --- a/x/oracle/keeper/historic_price_test.go +++ b/x/oracle/keeper/historic_price_test.go @@ -16,9 +16,6 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { // similar prefixes displayDenomVariation := displayDenom + "test" - availableMedians := app.OracleKeeper.AvailableMedians(ctx, displayDenom) - s.Require().Equal(availableMedians, uint32(0)) - // add multiple historic prices to store exchangeRates := []string{"1.0", "1.2", "1.1", "1.4", "1.1", "1.15", "1.2", "1.3", "1.2"} for i, exchangeRate := range exchangeRates { @@ -42,9 +39,6 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { s.Require().Equal(medians[1], sdk.MustNewDecFromStr("1.125")) s.Require().Equal(medians[2], sdk.MustNewDecFromStr("1.1")) - availableMedians = app.OracleKeeper.AvailableMedians(ctx, displayDenom) - s.Require().Equal(availableMedians, uint32(3)) - medianDeviation, err := app.OracleKeeper.HistoricMedianDeviation(ctx, displayDenom) s.Require().NoError(err) s.Require().Equal(medianDeviation, sdk.MustNewDecFromStr("0.111803398874989476")) @@ -96,9 +90,6 @@ func (s *IntegrationTestSuite) TestSetHistoraclePricing() { firstStampBlock := lastStampBlock - blockPeriod app.OracleKeeper.DeleteHistoricMedian(ctx, displayDenom, firstStampBlock) - availableMedians = app.OracleKeeper.AvailableMedians(ctx, displayDenom) - s.Require().Equal(availableMedians, uint32(2)) - medians = app.OracleKeeper.HistoricMedians(ctx, displayDenom, 3) s.Require().Equal(len(medians), 2) s.Require().Equal(medians[0], sdk.MustNewDecFromStr("1.2")) From 01ff5e4722185297153185254dfc9d6a70fb26d2 Mon Sep 17 00:00:00 2001 From: rbajollari Date: Thu, 15 Dec 2022 11:57:54 -0500 Subject: [PATCH 6/6] godoc --- x/oracle/keeper/historic_price.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x/oracle/keeper/historic_price.go b/x/oracle/keeper/historic_price.go index e1f15cf0a5..9261b84c57 100644 --- a/x/oracle/keeper/historic_price.go +++ b/x/oracle/keeper/historic_price.go @@ -135,6 +135,7 @@ func (k Keeper) SetHistoricMedianDeviation( // MedianOfHistoricMedians calculates and returns the median of the last stampNum // historic medians as well as the amount of medians used to calculate that median. +// If no medians are available, all returns are zero and error is nil. func (k Keeper) MedianOfHistoricMedians( ctx sdk.Context, denom string, @@ -154,6 +155,7 @@ func (k Keeper) MedianOfHistoricMedians( // AverageOfHistoricMedians calculates and returns the average of the last stampNum // historic medians as well as the amount of medians used to calculate that average. +// If no medians are available, all returns are zero and error is nil. func (k Keeper) AverageOfHistoricMedians( ctx sdk.Context, denom string, @@ -173,6 +175,7 @@ func (k Keeper) AverageOfHistoricMedians( // MaxOfHistoricMedian calculates and returns the maximum value of the last stampNum // historic medians as well as the amount of medians used to calculate that maximum. +// If no medians are available, all returns are zero and error is nil. func (k Keeper) MaxOfHistoricMedians( ctx sdk.Context, denom string, @@ -192,6 +195,7 @@ func (k Keeper) MaxOfHistoricMedians( // MinOfHistoricMedians calculates and returns the minimum value of the last stampNum // historic medians as well as the amount of medians used to calculate that minimum. +// If no medians are available, all returns are zero and error is nil. func (k Keeper) MinOfHistoricMedians( ctx sdk.Context, denom string,