From 26b5136584e648e22a4e40b7ed46dd2875f255ed Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 7 Jun 2022 17:30:52 -0500 Subject: [PATCH] Fix spotprice sigfig rounding for small spot prices (#1699) * Fix spotprice sigfig rounding for small spot prices * Changelog * Fix off by one in precision * Address PR comment (cherry picked from commit c211999848a3ae1c9ac50716949f01c3a1bb9cfe) # Conflicts: # CHANGELOG.md --- CHANGELOG.md | 17 +++++++++++++++++ app/apptesting/gamm.go | 2 +- osmomath/sigfig_round.go | 24 ++++++++++++++++++++++++ x/gamm/pool-models/balancer/amm.go | 3 ++- x/gamm/pool-models/balancer/amm_test.go | 14 ++++++++++++++ x/gamm/types/constants.go | 2 +- 6 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 osmomath/sigfig_round.go diff --git a/CHANGELOG.md b/CHANGELOG.md index cbdbd98d0e8..7159065164a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +<<<<<<< HEAD +======= +### Breaking Changes + +* [#1699](https://github.com/osmosis-labs/osmosis/pull/1699) Fixes bug in sig fig rounding on spot price queries for small values + +#### golang API breaks + +* [#1671](https://github.com/osmosis-labs/osmosis/pull/1671) Remove methods that constitute AppModuleSimulation APIs for several modules' AppModules, which implemented no-ops +* [#1671](https://github.com/osmosis-labs/osmosis/pull/1671) Add hourly epochs to `x/epochs` DefaultGenesis. +* [#1665](https://github.com/osmosis-labs/osmosis/pull/1665) Delete app/App interface, instead use simapp.App +* [#1630](https://github.com/osmosis-labs/osmosis/pull/1630) Delete the v043_temp module, now that we're on an updated SDK version. +* [#1667](https://github.com/osmosis-labs/osmosis/pull/1673) Move wasm-bindings code out of app . + +### Features + +>>>>>>> c211999 (Fix spotprice sigfig rounding for small spot prices (#1699)) * [#1312] Stableswap: Createpool logic * [#1230] Stableswap CFMM equations * [#1429] solver for multi-asset CFMM diff --git a/app/apptesting/gamm.go b/app/apptesting/gamm.go index 91a750a98b6..e8477f0f76a 100644 --- a/app/apptesting/gamm.go +++ b/app/apptesting/gamm.go @@ -45,7 +45,7 @@ func (suite *KeeperTestHelper) PrepareBalancerPool() uint64 { spotPrice, err = suite.App.GAMMKeeper.CalculateSpotPrice(suite.Ctx, poolId, "baz", "foo") suite.NoError(err) s := sdk.NewDec(1).Quo(sdk.NewDec(3)) - sp := s.Mul(gammtypes.SigFigs).RoundInt().ToDec().Quo(gammtypes.SigFigs) + sp := s.MulInt(gammtypes.SigFigs).RoundInt().ToDec().QuoInt(gammtypes.SigFigs) suite.Equal(sp.String(), spotPrice.String()) return poolId diff --git a/osmomath/sigfig_round.go b/osmomath/sigfig_round.go new file mode 100644 index 00000000000..cd8621bd18c --- /dev/null +++ b/osmomath/sigfig_round.go @@ -0,0 +1,24 @@ +package osmomath + +import sdk "github.com/cosmos/cosmos-sdk/types" + +var pointOne = sdk.OneDec().QuoInt64(10) + +func SigFigRound(d sdk.Dec, tenToSigFig sdk.Int) sdk.Dec { + // for d > .1, we do round(d * 10^sigfig) / 10^sigfig + // for k, where 10^k*d > .1 && 10^{k-1}*d < .1, we do: + // (round(10^k * d * 10^sigfig) / (10^sigfig * 10^k) + // take note of floor div, vs normal div + k := uint64(0) + dTimesK := d + for ; dTimesK.LT(pointOne); k += 1 { + dTimesK.MulInt64Mut(10) + } + // d * 10^k * 10^sigfig + dkSigFig := dTimesK.MulInt(tenToSigFig) + numerator := dkSigFig.RoundInt().ToDec() + + tenToK := sdk.NewInt(10).ToDec().Power(k) + denominator := tenToSigFig.Mul(tenToK.TruncateInt()) + return numerator.QuoInt(denominator) +} diff --git a/x/gamm/pool-models/balancer/amm.go b/x/gamm/pool-models/balancer/amm.go index 7133766ff9a..54004ad7c6a 100644 --- a/x/gamm/pool-models/balancer/amm.go +++ b/x/gamm/pool-models/balancer/amm.go @@ -195,7 +195,8 @@ func (p Pool) SpotPrice(ctx sdk.Context, baseAsset, quoteAsset string) (sdk.Dec, invWeightRatio := quote.Weight.ToDec().Quo(base.Weight.ToDec()) supplyRatio := base.Token.Amount.ToDec().Quo(quote.Token.Amount.ToDec()) fullRatio := supplyRatio.Mul(invWeightRatio) - ratio := (fullRatio.Mul(types.SigFigs).RoundInt()).ToDec().Quo(types.SigFigs) + // we want to round this to `SigFigs` of precision + ratio := osmomath.SigFigRound(fullRatio, types.SigFigs) return ratio, nil } diff --git a/x/gamm/pool-models/balancer/amm_test.go b/x/gamm/pool-models/balancer/amm_test.go index 49c3b4f296c..cda56f4940b 100644 --- a/x/gamm/pool-models/balancer/amm_test.go +++ b/x/gamm/pool-models/balancer/amm_test.go @@ -53,6 +53,20 @@ func (suite *KeeperTestSuite) TestBalancerSpotPrice() { expectError: false, expectedOutput: sdk.MustNewDecFromStr("1.913043480000000000"), // ans is 1.913043478260869565, rounded is 1.91304348 }, + { + name: "check number of sig figs", + baseDenomPoolInput: sdk.NewInt64Coin(baseDenom, 100), + quoteDenomPoolInput: sdk.NewInt64Coin(quoteDenom, 300), + expectError: false, + expectedOutput: sdk.MustNewDecFromStr("0.333333330000000000"), + }, + { + name: "check number of sig figs high sizes", + baseDenomPoolInput: sdk.NewInt64Coin(baseDenom, 343569192534), + quoteDenomPoolInput: sdk.NewCoin(quoteDenom, sdk.MustNewDecFromStr("186633424395479094888742").TruncateInt()), + expectError: false, + expectedOutput: sdk.MustNewDecFromStr("0.000000000001840877"), + }, } for _, tc := range tests { diff --git a/x/gamm/types/constants.go b/x/gamm/types/constants.go index 56cb99e2b0b..acdde3c66e2 100644 --- a/x/gamm/types/constants.go +++ b/x/gamm/types/constants.go @@ -22,5 +22,5 @@ var ( InitPoolSharesSupply = OneShare.MulRaw(100) // SigFigs is the amount of significant figures used to calculate SpotPrice - SigFigs = sdk.NewDec(10).Power(SigFigsExponent) + SigFigs = sdk.NewDec(10).Power(SigFigsExponent).TruncateInt() )