refactor: remove usage of global basedenom (#18268)

This commit is contained in:
Marko
2023-10-30 19:39:51 +00:00
committed by GitHub
parent 15ccdccea4
commit e6a4c78e73
8 changed files with 41 additions and 375 deletions
+19 -1
View File
@@ -881,7 +881,8 @@ func ParseCoinNormalized(coinStr string) (coin Coin, err error) {
return Coin{}, err
}
coin, _ = NormalizeDecCoin(decCoin).TruncateDecimal()
coin, _ = NewDecCoinFromDec(decCoin.Denom, decCoin.Amount).TruncateDecimal()
return coin, nil
}
@@ -900,3 +901,20 @@ func ParseCoinsNormalized(coinStr string) (Coins, error) {
}
return NormalizeCoins(coins), nil
}
// ----------------------------------------------------------------------------
// NormalizeCoins normalize and truncate a list of decimal coins
func NormalizeCoins(coins []DecCoin) Coins {
if coins == nil {
return nil
}
result := make([]Coin, 0, len(coins))
for _, coin := range coins {
newCoin, _ := NewDecCoinFromDec(coin.Denom, coin.Amount).TruncateDecimal()
result = append(result, newCoin)
}
return result
}
-160
View File
@@ -1,160 +0,0 @@
package types
import (
"fmt"
"cosmossdk.io/math"
)
// denomUnits contains a mapping of denomination mapped to their respective unit
// multipliers (e.g. 1atom = 10^-6uatom).
var denomUnits = map[string]math.LegacyDec{}
// baseDenom is the denom of smallest unit registered
var baseDenom string
// RegisterDenom registers a denomination with a corresponding unit. If the
// denomination is already registered, an error will be returned.
func RegisterDenom(denom string, unit math.LegacyDec) error {
if err := ValidateDenom(denom); err != nil {
return err
}
if _, ok := denomUnits[denom]; ok {
return fmt.Errorf("denom %s already registered", denom)
}
denomUnits[denom] = unit
if baseDenom == "" || unit.LT(denomUnits[baseDenom]) {
baseDenom = denom
}
return nil
}
// GetDenomUnit returns a unit for a given denomination if it exists. A boolean
// is returned if the denomination is registered.
func GetDenomUnit(denom string) (math.LegacyDec, bool) {
if err := ValidateDenom(denom); err != nil {
return math.LegacyZeroDec(), false
}
unit, ok := denomUnits[denom]
if !ok {
return math.LegacyZeroDec(), false
}
return unit, true
}
// SetBaseDenom allow overwritting the base denom
// if the denom has registered before, otherwise return error
func SetBaseDenom(denom string) error {
_, ok := denomUnits[denom]
if !ok {
return fmt.Errorf("denom %s not registered", denom)
}
baseDenom = denom
return nil
}
// GetBaseDenom returns the denom of smallest unit registered
func GetBaseDenom() (string, error) {
if baseDenom == "" {
return "", fmt.Errorf("no denom is registered")
}
return baseDenom, nil
}
// ConvertCoin attempts to convert a coin to a given denomination. If the given
// denomination is invalid or if neither denomination is registered, an error
// is returned.
func ConvertCoin(coin Coin, denom string) (Coin, error) {
if err := ValidateDenom(denom); err != nil {
return Coin{}, err
}
srcUnit, ok := GetDenomUnit(coin.Denom)
if !ok {
return Coin{}, fmt.Errorf("source denom not registered: %s", coin.Denom)
}
dstUnit, ok := GetDenomUnit(denom)
if !ok {
return Coin{}, fmt.Errorf("destination denom not registered: %s", denom)
}
if srcUnit.Equal(dstUnit) {
return NewCoin(denom, coin.Amount), nil
}
return NewCoin(denom, math.LegacyNewDecFromInt(coin.Amount).Mul(srcUnit).Quo(dstUnit).TruncateInt()), nil
}
// ConvertDecCoin attempts to convert a decimal coin to a given denomination. If the given
// denomination is invalid or if neither denomination is registered, an error
// is returned.
func ConvertDecCoin(coin DecCoin, denom string) (DecCoin, error) {
if err := ValidateDenom(denom); err != nil {
return DecCoin{}, err
}
srcUnit, ok := GetDenomUnit(coin.Denom)
if !ok {
return DecCoin{}, fmt.Errorf("source denom not registered: %s", coin.Denom)
}
dstUnit, ok := GetDenomUnit(denom)
if !ok {
return DecCoin{}, fmt.Errorf("destination denom not registered: %s", denom)
}
if srcUnit.Equal(dstUnit) {
return NewDecCoinFromDec(denom, coin.Amount), nil
}
return NewDecCoinFromDec(denom, coin.Amount.Mul(srcUnit).Quo(dstUnit)), nil
}
// NormalizeCoin try to convert a coin to the smallest unit registered,
// returns original one if failed.
func NormalizeCoin(coin Coin) Coin {
base, err := GetBaseDenom()
if err != nil {
return coin
}
newCoin, err := ConvertCoin(coin, base)
if err != nil {
return coin
}
return newCoin
}
// NormalizeDecCoin try to convert a decimal coin to the smallest unit registered,
// returns original one if failed.
func NormalizeDecCoin(coin DecCoin) DecCoin {
base, err := GetBaseDenom()
if err != nil {
return coin
}
newCoin, err := ConvertDecCoin(coin, base)
if err != nil {
return coin
}
return newCoin
}
// NormalizeCoins normalize and truncate a list of decimal coins
func NormalizeCoins(coins []DecCoin) Coins {
if coins == nil {
return nil
}
result := make([]Coin, 0, len(coins))
for _, coin := range coins {
newCoin, _ := NormalizeDecCoin(coin).TruncateDecimal()
result = append(result, newCoin)
}
return result
}
-211
View File
@@ -1,211 +0,0 @@
package types
import (
"testing"
"github.com/stretchr/testify/suite"
"cosmossdk.io/math"
)
var (
atom = "atom" // 1 (base denom unit)
matom = "matom" // 10^-3 (milli)
uatom = "uatom" // 10^-6 (micro)
natom = "natom" // 10^-9 (nano)
)
type internalDenomTestSuite struct {
suite.Suite
}
func TestInternalDenomTestSuite(t *testing.T) {
suite.Run(t, new(internalDenomTestSuite))
}
func (s *internalDenomTestSuite) TestRegisterDenom() {
atomUnit := math.LegacyOneDec() // 1 (base denom unit)
s.Require().NoError(RegisterDenom(atom, atomUnit))
s.Require().Error(RegisterDenom(atom, atomUnit))
res, ok := GetDenomUnit(atom)
s.Require().True(ok)
s.Require().Equal(atomUnit, res)
res, ok = GetDenomUnit(matom)
s.Require().False(ok)
s.Require().Equal(math.LegacyZeroDec(), res)
err := SetBaseDenom(atom)
s.Require().NoError(err)
res, ok = GetDenomUnit(atom)
s.Require().True(ok)
s.Require().Equal(atomUnit, res)
// reset registration
baseDenom = ""
denomUnits = map[string]math.LegacyDec{}
}
func (s *internalDenomTestSuite) TestConvertCoins() {
atomUnit := math.LegacyOneDec() // 1 (base denom unit)
s.Require().NoError(RegisterDenom(atom, atomUnit))
matomUnit := math.LegacyNewDecWithPrec(1, 3) // 10^-3 (milli)
s.Require().NoError(RegisterDenom(matom, matomUnit))
uatomUnit := math.LegacyNewDecWithPrec(1, 6) // 10^-6 (micro)
s.Require().NoError(RegisterDenom(uatom, uatomUnit))
natomUnit := math.LegacyNewDecWithPrec(1, 9) // 10^-9 (nano)
s.Require().NoError(RegisterDenom(natom, natomUnit))
res, err := GetBaseDenom()
s.Require().NoError(err)
s.Require().Equal(res, natom)
s.Require().Equal(NormalizeCoin(NewCoin(uatom, math.NewInt(1))), NewCoin(natom, math.NewInt(1000)))
s.Require().Equal(NormalizeCoin(NewCoin(matom, math.NewInt(1))), NewCoin(natom, math.NewInt(1000000)))
s.Require().Equal(NormalizeCoin(NewCoin(atom, math.NewInt(1))), NewCoin(natom, math.NewInt(1000000000)))
coins, err := ParseCoinsNormalized("1atom,1matom,1uatom")
s.Require().NoError(err)
s.Require().Equal(coins, Coins{
Coin{natom, math.NewInt(1000000000)},
Coin{natom, math.NewInt(1000000)},
Coin{natom, math.NewInt(1000)},
})
testCases := []struct {
input Coin
denom string
result Coin
expErr bool
}{
{NewCoin("foo", math.ZeroInt()), atom, Coin{}, true},
{NewCoin(atom, math.ZeroInt()), "foo", Coin{}, true},
{NewCoin(atom, math.ZeroInt()), "FOO", Coin{}, true},
{NewCoin(atom, math.NewInt(5)), matom, NewCoin(matom, math.NewInt(5000)), false}, // atom => matom
{NewCoin(atom, math.NewInt(5)), uatom, NewCoin(uatom, math.NewInt(5000000)), false}, // atom => uatom
{NewCoin(atom, math.NewInt(5)), natom, NewCoin(natom, math.NewInt(5000000000)), false}, // atom => natom
{NewCoin(uatom, math.NewInt(5000000)), matom, NewCoin(matom, math.NewInt(5000)), false}, // uatom => matom
{NewCoin(uatom, math.NewInt(5000000)), natom, NewCoin(natom, math.NewInt(5000000000)), false}, // uatom => natom
{NewCoin(uatom, math.NewInt(5000000)), atom, NewCoin(atom, math.NewInt(5)), false}, // uatom => atom
{NewCoin(matom, math.NewInt(5000)), natom, NewCoin(natom, math.NewInt(5000000000)), false}, // matom => natom
{NewCoin(matom, math.NewInt(5000)), uatom, NewCoin(uatom, math.NewInt(5000000)), false}, // matom => uatom
}
for i, tc := range testCases {
res, err := ConvertCoin(tc.input, tc.denom)
s.Require().Equal(
tc.expErr, err != nil,
"unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
)
s.Require().Equal(
tc.result, res,
"invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
)
}
// reset registration
baseDenom = ""
denomUnits = map[string]math.LegacyDec{}
}
func (s *internalDenomTestSuite) TestConvertDecCoins() {
atomUnit := math.LegacyOneDec() // 1 (base denom unit)
s.Require().NoError(RegisterDenom(atom, atomUnit))
matomUnit := math.LegacyNewDecWithPrec(1, 3) // 10^-3 (milli)
s.Require().NoError(RegisterDenom(matom, matomUnit))
uatomUnit := math.LegacyNewDecWithPrec(1, 6) // 10^-6 (micro)
s.Require().NoError(RegisterDenom(uatom, uatomUnit))
natomUnit := math.LegacyNewDecWithPrec(1, 9) // 10^-9 (nano)
s.Require().NoError(RegisterDenom(natom, natomUnit))
res, err := GetBaseDenom()
s.Require().NoError(err)
s.Require().Equal(res, natom)
s.Require().Equal(NormalizeDecCoin(NewDecCoin(uatom, math.NewInt(1))), NewDecCoin(natom, math.NewInt(1000)))
s.Require().Equal(NormalizeDecCoin(NewDecCoin(matom, math.NewInt(1))), NewDecCoin(natom, math.NewInt(1000000)))
s.Require().Equal(NormalizeDecCoin(NewDecCoin(atom, math.NewInt(1))), NewDecCoin(natom, math.NewInt(1000000000)))
coins, err := ParseCoinsNormalized("0.1atom,0.1matom,0.1uatom")
s.Require().NoError(err)
s.Require().Equal(coins, Coins{
Coin{natom, math.NewInt(100000000)},
Coin{natom, math.NewInt(100000)},
Coin{natom, math.NewInt(100)},
})
testCases := []struct {
input DecCoin
denom string
result DecCoin
expErr bool
}{
{NewDecCoin("foo", math.ZeroInt()), atom, DecCoin{}, true},
{NewDecCoin(atom, math.ZeroInt()), "foo", DecCoin{}, true},
{NewDecCoin(atom, math.ZeroInt()), "FOO", DecCoin{}, true},
// 0.5atom
{NewDecCoinFromDec(atom, math.LegacyNewDecWithPrec(5, 1)), matom, NewDecCoin(matom, math.NewInt(500)), false}, // atom => matom
{NewDecCoinFromDec(atom, math.LegacyNewDecWithPrec(5, 1)), uatom, NewDecCoin(uatom, math.NewInt(500000)), false}, // atom => uatom
{NewDecCoinFromDec(atom, math.LegacyNewDecWithPrec(5, 1)), natom, NewDecCoin(natom, math.NewInt(500000000)), false}, // atom => natom
{NewDecCoin(uatom, math.NewInt(5000000)), matom, NewDecCoin(matom, math.NewInt(5000)), false}, // uatom => matom
{NewDecCoin(uatom, math.NewInt(5000000)), natom, NewDecCoin(natom, math.NewInt(5000000000)), false}, // uatom => natom
{NewDecCoin(uatom, math.NewInt(5000000)), atom, NewDecCoin(atom, math.NewInt(5)), false}, // uatom => atom
{NewDecCoin(matom, math.NewInt(5000)), natom, NewDecCoin(natom, math.NewInt(5000000000)), false}, // matom => natom
{NewDecCoin(matom, math.NewInt(5000)), uatom, NewDecCoin(uatom, math.NewInt(5000000)), false}, // matom => uatom
}
for i, tc := range testCases {
res, err := ConvertDecCoin(tc.input, tc.denom)
s.Require().Equal(
tc.expErr, err != nil,
"unexpected error; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
)
s.Require().Equal(
tc.result, res,
"invalid result; tc: #%d, input: %s, denom: %s", i+1, tc.input, tc.denom,
)
}
// reset registration
baseDenom = ""
denomUnits = map[string]math.LegacyDec{}
}
func (s *internalDenomTestSuite) TestDecOperationOrder() {
dec, err := math.LegacyNewDecFromStr("11")
s.Require().NoError(err)
s.Require().NoError(RegisterDenom("unit1", dec))
dec, err = math.LegacyNewDecFromStr("100000011")
s.Require().NoError(err)
s.Require().NoError(RegisterDenom("unit2", dec))
coin, err := ConvertCoin(NewCoin("unit1", math.NewInt(100000011)), "unit2")
s.Require().NoError(err)
s.Require().Equal(coin, NewCoin("unit2", math.NewInt(11)))
// reset registration
baseDenom = ""
denomUnits = map[string]math.LegacyDec{}
}
func (s *internalDenomTestSuite) TestSetBaseDenomError() {
err := SetBaseDenom(atom)
s.Require().Error(err)
// reset registration
baseDenom = ""
denomUnits = map[string]math.LegacyDec{}
}