From 8802e9095eedc6c70bbf5d421128bdfb043e2db5 Mon Sep 17 00:00:00 2001 From: bbist Date: Wed, 1 Sep 2021 12:32:59 +0545 Subject: [PATCH 1/3] added formatting helpers for balance (zcn,mzcn,uzcn,sas) #10 --- zcncore/zcnfmt.go | 121 +++++++++++++++++++++++++++++++++++++++++ zcncore/zcnfmt_test.go | 22 ++++++++ 2 files changed, 143 insertions(+) create mode 100644 zcncore/zcnfmt.go create mode 100644 zcncore/zcnfmt_test.go diff --git a/zcncore/zcnfmt.go b/zcncore/zcnfmt.go new file mode 100644 index 000000000..1f46b750a --- /dev/null +++ b/zcncore/zcnfmt.go @@ -0,0 +1,121 @@ +package zcncore + +import ( + "errors" + "fmt" + "regexp" + "strconv" +) + +const ( + SAS Unit = iota + UZCN + MZCN + ZCN +) + +var ( + ErrUndefinedFormat = errors.New("undefined format") + + // regexp to parse zcn + reParseZCN = regexp.MustCompile(`(\d+)\s+(SAS|sas|uZCN|uzcn|mZCN|mzcn|ZCN|zcn)`) +) + +type Unit byte + +func (unit Unit) String() string { + switch unit { + case SAS: + return "SAS" + case MZCN: + return "mZCN" + case UZCN: + return "uZCN" + } + return "ZCN" +} + +func (unit *Unit) Parse(s string) error { + switch s { + case "SAS", "sas": + *unit = SAS + case "uZCN", "uzcn": + *unit = UZCN + case "mZCN", "mzcn": + *unit = MZCN + case "ZCN", "zcn": + *unit = ZCN + default: + return ErrUndefinedFormat + } + return nil +} + +func ParseUnit(s string) (Unit, error) { + var unit Unit + err := unit.Parse(s) + if err != nil { + return unit, err + } + return unit, nil +} + +// Balance is a type used for 0chain native token +type Balance int64 + +func (token Balance) Format(unit Unit) string { + switch unit { + case SAS: + return fmt.Sprintf("%d SAS", token) + case UZCN: + return fmt.Sprintf("%.3f uZCN", float64(token)/1e3) + case MZCN: + return fmt.Sprintf("%.3f mZCN", float64(token)/1e6) + } + return fmt.Sprintf("%.3f ZCN", float64(token)/1e9) +} + +func (token Balance) AutoFormat() string { + switch { + case token/1e9 > 0: + return token.Format(ZCN) + case token/1e6 > 0: + return token.Format(MZCN) + case token/1e3 > 0: + return token.Format(UZCN) + } + return token.Format(SAS) +} + +func Format(token Balance, unit Unit) string { + return token.Format(unit) +} + +func AutoFormat(token Balance) string { + return token.AutoFormat() +} + +func Parse(zcnstr string) (Balance, error) { + + matches := reParseZCN.FindAllStringSubmatch(zcnstr, 1) + + if len(matches) != 1 { + return 0, fmt.Errorf("invalid input: %s", zcnstr) + } + + token, err := strconv.ParseInt(matches[0][0], 10, 64) + if err != nil { + return 0, err + } + + switch matches[0][1] { + case "uZCN", "uzcn": + token *= 10e3 + case "mZCN", "mzcn": + token *= 10e6 + case "ZCN", "zcn": + token *= 10e9 + } + + return Balance(token), nil +} diff --git a/zcncore/zcnfmt_test.go b/zcncore/zcnfmt_test.go new file mode 100644 index 000000000..a6935a23d --- /dev/null +++ b/zcncore/zcnfmt_test.go @@ -0,0 +1,22 @@ +package zcncore + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFormat(t *testing.T) { + token := Balance(129382129321) + require.Equal(t, "129.382 ZCN", token.Format(ZCN)) + require.Equal(t, "129382.129 mZCN", token.Format(MZCN)) + require.Equal(t, "129382129.321 uZCN", token.Format(UZCN)) + require.Equal(t, "129382129321 SAS", token.Format(SAS)) +} + +func TestAutoFormat(t *testing.T) { + require.Equal(t, "239 SAS", Balance(239).AutoFormat()) + require.Equal(t, "27.361 uZCN", Balance(27361).AutoFormat()) + require.Equal(t, "23.872 mZCN", Balance(23872013).AutoFormat()) + require.Equal(t, "203.827 ZCN", Balance(203827162834).AutoFormat()) +} From b4bb8b1bab79640a8bb084e5b86abaacbd95e848 Mon Sep 17 00:00:00 2001 From: bbist Date: Wed, 8 Sep 2021 16:05:37 +0545 Subject: [PATCH 2/3] updated formatting helpers and refactored balance type --- core/common/misc.go | 152 +++++++++++++++++++++++++++++++++------ core/common/misc_test.go | 49 +++++++++++++ zcncore/zcnfmt.go | 121 ------------------------------- zcncore/zcnfmt_test.go | 22 ------ 4 files changed, 181 insertions(+), 163 deletions(-) create mode 100644 core/common/misc_test.go delete mode 100644 zcncore/zcnfmt.go delete mode 100644 zcncore/zcnfmt_test.go diff --git a/core/common/misc.go b/core/common/misc.go index 572548163..797d16d8a 100644 --- a/core/common/misc.go +++ b/core/common/misc.go @@ -2,32 +2,13 @@ package common import ( "fmt" + "regexp" "strconv" "strings" "github.com/0chain/errors" ) -const tokenUnit = 10000000000.0 - -// Balance represents an amount of tokens. -type Balance int64 - -// ToToken converts the Balance to ZCN tokens. -func (b Balance) ToToken() float64 { - return float64(b) / tokenUnit -} - -// ToBalance converts ZCN tokens to Balance. -func ToBalance(tok float64) Balance { - return Balance(tok * tokenUnit) -} - -// String implements fmt.Stringer interface. -func (b Balance) String() string { - return strconv.FormatFloat(b.ToToken(), 'f', -1, 64) -} - // A Key represents an identifier. It can be a pool ID, client ID, smart // contract address, etc. type Key string @@ -95,3 +76,134 @@ func (wp *WhoPays) Parse(val string) (err error) { } return } + +/* Balance */ + +// minimum token unit (sas) +const tokenUnit = 1e10 + +// reParseToken is a regexp to parse string representation of token +var reParseToken = regexp.MustCompile(`^((?:\d*\.)?\d+)\s+(SAS|sas|uZCN|uzcn|mZCN|mzcn|ZCN|zcn)$`) + +// Balance represents 0chain native token +type Balance int64 + +func (b Balance) ToToken() float64 { + return float64(b) / tokenUnit +} + +// String implements fmt.Stringer interface. +func (b Balance) String() string { + return b.AutoFormat() +} + +func (b Balance) Format(unit BalanceUnit) string { + v := float64(b) + switch unit { + case SAS: + v /= 1e0 + case UZCN: + v /= 1e4 + case MZCN: + v /= 1e7 + case ZCN: + v /= 1e10 + } + return fmt.Sprintf("%.3f %v", v, unit) +} + +func (b Balance) AutoFormat() string { + switch { + case b/1e10 > 0: + return b.Format(ZCN) + case b/1e7 > 0: + return b.Format(MZCN) + case b/1e4 > 0: + return b.Format(UZCN) + } + return b.Format(SAS) +} + +// ToBalance converts ZCN tokens to Balance. +func ToBalance(token float64) Balance { + return Balance(token * tokenUnit) +} + +func FormatBalance(b Balance, unit BalanceUnit) string { + return b.Format(unit) +} + +func AutoFormatBalance(b Balance) string { + return b.AutoFormat() +} + +func ParseBalance(str string) (Balance, error) { + + matches := reParseToken.FindAllStringSubmatch(str, -1) + + if len(matches) != 1 || len(matches[0]) != 3 { + return 0, fmt.Errorf("invalid input: %s", str) + } + + b, err := strconv.ParseFloat(matches[0][1], 64) + if err != nil { + return 0, err + } + + var unit BalanceUnit + + err = unit.Parse(matches[0][2]) + if err != nil { + return 0, err + } + + switch unit { + case UZCN: + b *= 1e4 + case MZCN: + b *= 1e7 + case ZCN: + b *= 1e10 + } + + return Balance(b), nil +} + +const ( + SAS BalanceUnit = iota + UZCN + MZCN + ZCN +) + +type BalanceUnit byte + +func (unit BalanceUnit) String() string { + switch unit { + case SAS: + return "SAS" + case MZCN: + return "mZCN" + case UZCN: + return "uZCN" + case ZCN: + return "ZCN" + } + return "" +} + +func (unit *BalanceUnit) Parse(s string) error { + switch s { + case "SAS", "sas": + *unit = SAS + case "uZCN", "uzcn": + *unit = UZCN + case "mZCN", "mzcn": + *unit = MZCN + case "ZCN", "zcn": + *unit = ZCN + default: + return errors.New("", "undefined balance unit: "+s) + } + return nil +} diff --git a/core/common/misc_test.go b/core/common/misc_test.go new file mode 100644 index 000000000..f84b38025 --- /dev/null +++ b/core/common/misc_test.go @@ -0,0 +1,49 @@ +package common + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFormat(t *testing.T) { + token := Balance(129382129321) + require.Equal(t, "12.938 ZCN", token.Format(ZCN)) + require.Equal(t, "12938.213 mZCN", token.Format(MZCN)) + require.Equal(t, "12938212.932 uZCN", token.Format(UZCN)) + require.Equal(t, "129382129321.000 SAS", token.Format(SAS)) +} + +func TestAutoFormat(t *testing.T) { + require.Equal(t, "239.000 SAS", Balance(239).AutoFormat()) + require.Equal(t, "2.736 uZCN", Balance(27361).AutoFormat()) + require.Equal(t, "2.387 mZCN", Balance(23872013).AutoFormat()) + require.Equal(t, "20.383 ZCN", Balance(203827162834).AutoFormat()) +} + +func TestParseBalance(t *testing.T) { + b, err := ParseBalance("12.938 ZCN") + require.NoError(t, err) + require.Equal(t, Balance(12.938*1e10), b) + + b, err = ParseBalance("12.938 mzcn") + require.NoError(t, err) + require.Equal(t, Balance(12.938*1e7), b) + + b, err = ParseBalance("12.938 uZCN") + require.NoError(t, err) + require.Equal(t, Balance(12.938*1e4), b) + + b, err = ParseBalance("122389 sas") + require.NoError(t, err) + require.Equal(t, Balance(122389*1e0), b) + + _, err = ParseBalance("10 ") + require.EqualError(t, err, "invalid input: 10 ") + + _, err = ParseBalance("10 zwe") + require.EqualError(t, err, "invalid input: 10 zwe") + + _, err = ParseBalance(" 10 zcn ") + require.EqualError(t, err, "invalid input: 10 zcn ") +} diff --git a/zcncore/zcnfmt.go b/zcncore/zcnfmt.go deleted file mode 100644 index 1f46b750a..000000000 --- a/zcncore/zcnfmt.go +++ /dev/null @@ -1,121 +0,0 @@ -package zcncore - -import ( - "errors" - "fmt" - "regexp" - "strconv" -) - -const ( - SAS Unit = iota - UZCN - MZCN - ZCN -) - -var ( - ErrUndefinedFormat = errors.New("undefined format") - - // regexp to parse zcn - reParseZCN = regexp.MustCompile(`(\d+)\s+(SAS|sas|uZCN|uzcn|mZCN|mzcn|ZCN|zcn)`) -) - -type Unit byte - -func (unit Unit) String() string { - switch unit { - case SAS: - return "SAS" - case MZCN: - return "mZCN" - case UZCN: - return "uZCN" - } - return "ZCN" -} - -func (unit *Unit) Parse(s string) error { - switch s { - case "SAS", "sas": - *unit = SAS - case "uZCN", "uzcn": - *unit = UZCN - case "mZCN", "mzcn": - *unit = MZCN - case "ZCN", "zcn": - *unit = ZCN - default: - return ErrUndefinedFormat - } - return nil -} - -func ParseUnit(s string) (Unit, error) { - var unit Unit - err := unit.Parse(s) - if err != nil { - return unit, err - } - return unit, nil -} - -// Balance is a type used for 0chain native token -type Balance int64 - -func (token Balance) Format(unit Unit) string { - switch unit { - case SAS: - return fmt.Sprintf("%d SAS", token) - case UZCN: - return fmt.Sprintf("%.3f uZCN", float64(token)/1e3) - case MZCN: - return fmt.Sprintf("%.3f mZCN", float64(token)/1e6) - } - return fmt.Sprintf("%.3f ZCN", float64(token)/1e9) -} - -func (token Balance) AutoFormat() string { - switch { - case token/1e9 > 0: - return token.Format(ZCN) - case token/1e6 > 0: - return token.Format(MZCN) - case token/1e3 > 0: - return token.Format(UZCN) - } - return token.Format(SAS) -} - -func Format(token Balance, unit Unit) string { - return token.Format(unit) -} - -func AutoFormat(token Balance) string { - return token.AutoFormat() -} - -func Parse(zcnstr string) (Balance, error) { - - matches := reParseZCN.FindAllStringSubmatch(zcnstr, 1) - - if len(matches) != 1 { - return 0, fmt.Errorf("invalid input: %s", zcnstr) - } - - token, err := strconv.ParseInt(matches[0][0], 10, 64) - if err != nil { - return 0, err - } - - switch matches[0][1] { - case "uZCN", "uzcn": - token *= 10e3 - case "mZCN", "mzcn": - token *= 10e6 - case "ZCN", "zcn": - token *= 10e9 - } - - return Balance(token), nil -} diff --git a/zcncore/zcnfmt_test.go b/zcncore/zcnfmt_test.go deleted file mode 100644 index a6935a23d..000000000 --- a/zcncore/zcnfmt_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package zcncore - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestFormat(t *testing.T) { - token := Balance(129382129321) - require.Equal(t, "129.382 ZCN", token.Format(ZCN)) - require.Equal(t, "129382.129 mZCN", token.Format(MZCN)) - require.Equal(t, "129382129.321 uZCN", token.Format(UZCN)) - require.Equal(t, "129382129321 SAS", token.Format(SAS)) -} - -func TestAutoFormat(t *testing.T) { - require.Equal(t, "239 SAS", Balance(239).AutoFormat()) - require.Equal(t, "27.361 uZCN", Balance(27361).AutoFormat()) - require.Equal(t, "23.872 mZCN", Balance(23872013).AutoFormat()) - require.Equal(t, "203.827 ZCN", Balance(203827162834).AutoFormat()) -} From 0040efe7fd8c8e78ab4d33c71a2870c1fb230bd2 Mon Sep 17 00:00:00 2001 From: bbist Date: Wed, 8 Sep 2021 17:06:54 +0545 Subject: [PATCH 3/3] removed decimal formatting from sas --- core/common/misc.go | 2 +- core/common/misc_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/common/misc.go b/core/common/misc.go index 797d16d8a..86d7c31a1 100644 --- a/core/common/misc.go +++ b/core/common/misc.go @@ -101,7 +101,7 @@ func (b Balance) Format(unit BalanceUnit) string { v := float64(b) switch unit { case SAS: - v /= 1e0 + return fmt.Sprintf("%d %v", b, unit) case UZCN: v /= 1e4 case MZCN: diff --git a/core/common/misc_test.go b/core/common/misc_test.go index f84b38025..f489564b3 100644 --- a/core/common/misc_test.go +++ b/core/common/misc_test.go @@ -11,11 +11,11 @@ func TestFormat(t *testing.T) { require.Equal(t, "12.938 ZCN", token.Format(ZCN)) require.Equal(t, "12938.213 mZCN", token.Format(MZCN)) require.Equal(t, "12938212.932 uZCN", token.Format(UZCN)) - require.Equal(t, "129382129321.000 SAS", token.Format(SAS)) + require.Equal(t, "129382129321 SAS", token.Format(SAS)) } func TestAutoFormat(t *testing.T) { - require.Equal(t, "239.000 SAS", Balance(239).AutoFormat()) + require.Equal(t, "239 SAS", Balance(239).AutoFormat()) require.Equal(t, "2.736 uZCN", Balance(27361).AutoFormat()) require.Equal(t, "2.387 mZCN", Balance(23872013).AutoFormat()) require.Equal(t, "20.383 ZCN", Balance(203827162834).AutoFormat())