From 4278dd30f2e973ff03d30188b081b16ca09c4c77 Mon Sep 17 00:00:00 2001 From: Brendan Chou <3680392+BrendanChou@users.noreply.github.com> Date: Tue, 21 May 2024 15:10:10 -0400 Subject: [PATCH] Improve `QuoteToBaseQuantums` (#1554) --- protocol/lib/quantums.go | 35 ++++++++++++++--------------------- protocol/lib/quantums_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/protocol/lib/quantums.go b/protocol/lib/quantums.go index be3d21309e..cf5634c36a 100644 --- a/protocol/lib/quantums.go +++ b/protocol/lib/quantums.go @@ -63,37 +63,30 @@ func BaseToQuoteQuantums( // quoteQuantums / priceValue / // 10^(priceExponent + baseCurrencyAtomicResolution - quoteCurrencyAtomicResolution) // -// The result is rounded down. +// The result is rounded towards zero. func QuoteToBaseQuantums( bigQuoteQuantums *big.Int, baseCurrencyAtomicResolution int32, priceValue uint64, priceExponent int32, ) (bigNotional *big.Int) { - // Determine the non-exponent part of the equation. - // We perform all calculations using positive rationals for consistent rounding. - isLong := bigQuoteQuantums.Sign() >= 0 - ratAbsQuoteQuantums := new(big.Rat).Abs( - new(big.Rat).SetInt(bigQuoteQuantums), - ) - ratPrice := new(big.Rat).SetUint64(priceValue) - ratQuoteQuantumsDivPrice := new(big.Rat).Quo(ratAbsQuoteQuantums, ratPrice) + // Initialize result to quoteQuantums. + result := new(big.Int).Set(bigQuoteQuantums) - // Determine the absolute value of the return value. + // Divide result (towards zero) by 10^(exponent). exponent := priceExponent + baseCurrencyAtomicResolution - QuoteCurrencyAtomicResolution - ratBaseQuantums := new(big.Rat).Quo( - ratQuoteQuantumsDivPrice, - RatPow10(exponent), - ) + power10Exponent := BigPow10(uint64(AbsInt32(exponent))) + if exponent > 0 { + result.Quo(result, power10Exponent) + } else { + result.Mul(result, power10Exponent) + } - // Round down. - bigBaseQuantums := BigRatRound(ratBaseQuantums, false) + // Divide result (towards zero) by priceValue. + // If there are two divisions, it is okay to do them separately as the result is the same. + result.Quo(result, new(big.Int).SetUint64(priceValue)) - // Flip the sign of the return value if necessary. - if !isLong { - bigBaseQuantums.Neg(bigBaseQuantums) - } - return bigBaseQuantums + return result } // multiplyByPrice multiples a value by price, factoring in exponents of base diff --git a/protocol/lib/quantums_test.go b/protocol/lib/quantums_test.go index a7818400f7..c2d8d5d283 100644 --- a/protocol/lib/quantums_test.go +++ b/protocol/lib/quantums_test.go @@ -251,3 +251,33 @@ func BenchmarkBaseToQuoteQuantums(b *testing.B) { expected2, _ := new(big.Int).SetString("2276555874282755105905825041901000000000", 10) require.Equal(b, expected2, result2) } + +func BenchmarkQuoteToBaseQuantums(b *testing.B) { + value, _ := new(big.Int).SetString("18446744073709551610", 10) + baseCurrencyAtomicResolution := int32(-8) + priceValue := uint64(1234123412341) + priceExponent1 := int32(-10) + priceExponent2 := int32(6) + var result1 *big.Int + var result2 *big.Int + + for i := 0; i < b.N; i++ { + result1 = lib.QuoteToBaseQuantums( + value, + baseCurrencyAtomicResolution, + priceValue, + priceExponent1, + ) + result2 = lib.QuoteToBaseQuantums( + value, + baseCurrencyAtomicResolution, + priceValue, + priceExponent2, + ) + } + + expected1, _ := new(big.Int).SetString("14947244245790664347", 10) + require.Equal(b, expected1, result1) + expected2, _ := new(big.Int).SetString("1494", 10) + require.Equal(b, expected2, result2) +}