diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a6df571d9f..65350e844c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +* [#6427](https://github.com/osmosis-labs/osmosis/pull/6427) sdk.Coins Mul and Quo helpers in osmoutils * [#6416](https://github.com/osmosis-labs/osmosis/pull/6416) feat[CL]: add num initialized ticks query ### Bug Fixes diff --git a/osmoutils/coins/coin_math.go b/osmoutils/coins/coin_math.go new file mode 100644 index 00000000000..009a036e9e8 --- /dev/null +++ b/osmoutils/coins/coin_math.go @@ -0,0 +1,138 @@ +package coins + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/osmosis-labs/osmosis/osmomath" +) + +// Mutative helpers that mutate the input coins + +// MulIntMut multiplies the coins by the given integer +// Mutates the input coins +func MulIntMut(coins sdk.Coins, num osmomath.Int) { + for i := range coins { + coins[i].Amount = coins[i].Amount.Mul(num) + } +} + +// MulRawMut multiplies the coins by the given integer +// Mutates the input coins +func MulRawMut(coins sdk.Coins, num int64) sdk.Coins { + for i := range coins { + coins[i].Amount = coins[i].Amount.MulRaw(num) + } + return coins +} + +// MulDecMut multiplies the coins by the given decimal +// Mutates the input coins +func MulDecMut(coins sdk.Coins, num osmomath.Dec) { + for i := range coins { + coins[i].Amount = coins[i].Amount.ToLegacyDec().Mul(num).TruncateInt() + } +} + +// QuoIntMut divides the coins by the given integer +// Mutates the input coins +func QuoIntMut(coins sdk.Coins, num osmomath.Int) { + for i := range coins { + coins[i].Amount = coins[i].Amount.Quo(num) + } +} + +// QuoRawMut divides the coins by the given integer +// Mutates the input coins +func QuoRawMut(coins sdk.Coins, num int64) { + for i := range coins { + coins[i].Amount = coins[i].Amount.QuoRaw(num) + } +} + +// QuoIntMut divides the coins by the given decimal +// Mutates the input coins +func QuoDecMut(coins sdk.Coins, num osmomath.Dec) { + for i := range coins { + coins[i].Amount = coins[i].Amount.ToLegacyDec().Quo(num).TruncateInt() + } +} + +// Non-mutative coin helpers that reallocate and return new coins + +// MulInt multiplies the coins by the given integer +// Does not mutate the input coins and returns new coins. +func MulInt(coins sdk.Coins, num osmomath.Int) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.Mul(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// MulRaw multiplies the coins by the given integer +// Does not mutate the input coins and returns new coins. +func MulRaw(coins sdk.Coins, num int64) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.MulRaw(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// MulDec multiplies the coins by the given decimal +// Does not mutate the input coins and returns new coins. +func MulDec(coins sdk.Coins, num osmomath.Dec) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.ToLegacyDec().Mul(num).TruncateInt() + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// QuoInt divides the coins by the given integer +// Does not mutate the input coins and returns new coins. +func QuoInt(coins sdk.Coins, num osmomath.Int) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.Quo(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// QuoRaw divides the coins by the given integer +// Does not mutate the input coins and returns new coins. +func QuoRaw(coins sdk.Coins, num int64) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.QuoRaw(num) + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} + +// QuoDec divides the coins by the given integer +// Does not mutate the input coins and returns new coins. +func QuoDec(coins sdk.Coins, num osmomath.Dec) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + + for i := range coins { + newCoins[i].Amount = coins[i].Amount.ToLegacyDec().Quo(num).TruncateInt() + newCoins[i].Denom = coins[i].Denom + } + + return newCoins +} diff --git a/osmoutils/coins/coin_math_test.go b/osmoutils/coins/coin_math_test.go new file mode 100644 index 00000000000..7e8a7fae160 --- /dev/null +++ b/osmoutils/coins/coin_math_test.go @@ -0,0 +1,135 @@ +package coins_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/osmosis-labs/osmosis/osmomath" + "github.com/osmosis-labs/osmosis/osmoutils/coins" +) + +var ( + defaultCoins = sdk.NewCoins( + sdk.NewCoin("foo", sdk.NewInt(100)), + sdk.NewCoin("bar", sdk.NewInt(200)), + ) + + defaultMultiplier = osmomath.NewInt(2) + + defaultMulExpectedResult = sdk.NewCoins( + sdk.NewCoin(defaultCoins[0].Denom, defaultCoins[0].Amount.Mul(defaultMultiplier)), + sdk.NewCoin(defaultCoins[1].Denom, defaultCoins[1].Amount.Mul(defaultMultiplier)), + ) + + defaultQuoExpectedResult = sdk.NewCoins( + sdk.NewCoin(defaultCoins[0].Denom, defaultCoins[0].Amount.Quo(defaultMultiplier)), + sdk.NewCoin(defaultCoins[1].Denom, defaultCoins[1].Amount.Quo(defaultMultiplier)), + ) +) + +// makes a deep copy to avoid accidentally mutating the input to a test. +func deepCopy(coins sdk.Coins) sdk.Coins { + newCoins := make(sdk.Coins, len(coins)) + for i := range coins { + newCoins[i].Amount = coins[i].Amount + newCoins[i].Denom = coins[i].Denom + } + return newCoins +} + +// Basic multiplication test. +func TestMul(t *testing.T) { + t.Run("test mutative multiplication", func(t *testing.T) { + t.Run("MulIntMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coins.MulIntMut(defaulCoins, defaultMultiplier) + require.Equal(t, defaultMulExpectedResult, defaulCoins) + }) + + t.Run("MulIntRawMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coins.MulRawMut(defaulCoins, defaultMultiplier.Int64()) + require.Equal(t, defaultMulExpectedResult, defaulCoins) + }) + + t.Run("MulDecMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coins.MulDecMut(defaulCoins, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultMulExpectedResult, defaulCoins) + }) + }) + + // Make a deep copy of the default coins for the input. + // Validate that the copy input coins are not mutated. + t.Run("test non-mutative multiplication", func(t *testing.T) { + t.Run("MulInt", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coins.MulInt(defaulCoinsCopy, defaultMultiplier) + require.Equal(t, defaultMulExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("MulIntRaw", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coins.MulRaw(defaulCoinsCopy, defaultMultiplier.Int64()) + require.Equal(t, defaultMulExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("MulDec", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coins.MulDec(defaulCoinsCopy, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultMulExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + }) +} + +func TestQuo(t *testing.T) { + t.Run("test mutative division", func(t *testing.T) { + t.Run("QuoIntMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coins.QuoIntMut(defaulCoins, defaultMultiplier) + require.Equal(t, defaultQuoExpectedResult, defaulCoins) + }) + + t.Run("QuoIntRawMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coins.QuoRawMut(defaulCoins, defaultMultiplier.Int64()) + require.Equal(t, defaultQuoExpectedResult, defaulCoins) + }) + + t.Run("QuoDecMut", func(t *testing.T) { + defaulCoins := deepCopy(defaultCoins) + coins.QuoDecMut(defaulCoins, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultQuoExpectedResult, defaulCoins) + }) + }) + + // Make a deep copy of the default coins for the input. + // Validate that the copy input coins are not mutated. + t.Run("test non-mutative division", func(t *testing.T) { + t.Run("QuoInt", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coins.QuoInt(defaulCoinsCopy, defaultMultiplier) + require.Equal(t, defaultQuoExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("QuoIntRaw", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coins.QuoRaw(defaulCoinsCopy, defaultMultiplier.Int64()) + require.Equal(t, defaultQuoExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + + t.Run("QuoDec", func(t *testing.T) { + defaulCoinsCopy := deepCopy(defaultCoins) + result := coins.QuoDec(defaulCoinsCopy, osmomath.NewDecFromInt(defaultMultiplier)) + require.Equal(t, defaultQuoExpectedResult, result) + require.Equal(t, defaultCoins, defaulCoinsCopy) + }) + }) +} diff --git a/osmoutils/go.sum b/osmoutils/go.sum index 58539856b6c..17b74fa94e1 100644 --- a/osmoutils/go.sum +++ b/osmoutils/go.sum @@ -41,6 +41,7 @@ collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= cosmossdk.io/errors v1.0.0 h1:nxF07lmlBbB8NKQhtJ+sJm6ef5uV1XkvPXG2bUntb04= cosmossdk.io/errors v1.0.0/go.mod h1:+hJZLuhdDE0pYN8HkOrVNwrIOYvUGnn6+4fjnJs/oV0= cosmossdk.io/math v1.1.3-rc.0 h1:lut7IbQGuqog4vEIHSB7ivtwwIr6rHrt5yoVowmtoDs= +cosmossdk.io/math v1.1.3-rc.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= @@ -148,6 +149,7 @@ github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc= +github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -706,6 +708,7 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/osmosis-labs/cosmos-sdk v0.45.0-rc1.0.20230913192254-a2075590d5a3 h1:kRNCIZn9NU8BBObAsFj0Zp05QQo/4iLDfRiXgtXF//I= +github.com/osmosis-labs/cosmos-sdk v0.45.0-rc1.0.20230913192254-a2075590d5a3/go.mod h1:mFGH2RJhuZa6vBuFMNe4JN85fGtIhROtdeQIyIw9isA= github.com/osmosis-labs/osmosis/osmomath v0.0.7-0.20230907220438-2f6dd779bc6d h1:b8Bz31kbzTbcSNsBOcvc2CRVJz5C3cGOQB7JqWDzdfA= github.com/osmosis-labs/osmosis/osmomath v0.0.7-0.20230907220438-2f6dd779bc6d/go.mod h1:Jj4zQ/IA8iPuHF+Hc/dTFJqAis5WYX3EcO7Xg70fAd8= github.com/osmosis-labs/osmosis/v19 v19.0.0 h1:gqcas/XfxtEuZXsWGTO9vNMHiY78Qs09FBQw73djIVM=