Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added formatting helpers for balance (zcn,mzcn,uzcn,sas) #10 #225

Merged
merged 3 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 132 additions & 20 deletions core/common/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
return fmt.Sprintf("%d %v", b, unit)
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
}
49 changes: 49 additions & 0 deletions core/common/misc_test.go
Original file line number Diff line number Diff line change
@@ -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 SAS", token.Format(SAS))
}

func TestAutoFormat(t *testing.T) {
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())
}

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 ")
}