Skip to content

Commit

Permalink
feat: support for number format and rounding
Browse files Browse the repository at this point in the history
  • Loading branch information
laojianzi committed May 19, 2021
1 parent 24e128f commit 8bbfadb
Show file tree
Hide file tree
Showing 11 changed files with 2,092 additions and 22 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ issues:
- path: _test\.go
linters:
- gomnd
- funlen

- linters:
- gomnd
Expand Down
7 changes: 7 additions & 0 deletions constants/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ const (
RoundUp
)

// Valid check this rounding mode is valid
func (r Rounding) Valid() bool {
return r == RoundDown ||
r == RoundHalfUp ||
r == RoundUp
}

const (
Decimals18 = 18
Univ2Symbol = "UNI-V2"
Expand Down
30 changes: 21 additions & 9 deletions entities/fraction.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package entities

import (
"fmt"
"math/big"

"github.com/shopspring/decimal"

"github.com/miraclesu/uniswap-sdk-go/constants"
"github.com/miraclesu/uniswap-sdk-go/number"
)

var (
Expand All @@ -16,6 +16,8 @@ var (
type Fraction struct {
Numerator *big.Int
Denominator *big.Int

opts *number.Options
}

func NewFraction(num, deno *big.Int) *Fraction {
Expand Down Expand Up @@ -103,15 +105,25 @@ func (f *Fraction) Divide(other *Fraction) *Fraction {
)
}

// NOTE: format, rounding
// TODO
func (f *Fraction) ToSignificant(significantDigits uint) string {
func (f *Fraction) ToSignificant(significantDigits uint, opt ...number.Option) string {
f.opts = number.New(number.WithGroupSeparator('\xA0'), number.WithRoundingMode(constants.RoundHalfUp))
f.opts.Apply(opt...)
f.opts.Apply(number.WithRoundingPrecision(int(significantDigits) + 1))

d := decimal.NewFromBigInt(big.NewInt(0).Div(f.Numerator, f.Denominator), 0)
return fmt.Sprintf("%v", d)
if v, err := number.DecimalRound(d, f.opts); err == nil {
d = v
}

return number.DecimalFormat(d, f.opts)
}

// TODO
func (f *Fraction) ToFixed(decimalPlaces uint) string {
d := big.NewInt(0).Div(f.Numerator, f.Denominator)
return fmt.Sprintf("%v", d)
func (f *Fraction) ToFixed(decimalPlaces uint, opt ...number.Option) string {
f.opts = number.New(number.WithGroupSeparator('\xA0'), number.WithRoundingMode(constants.RoundHalfUp))
f.opts.Apply(opt...)
f.opts.Apply(number.WithDecimalPlaces(decimalPlaces))

d := decimal.NewFromBigInt(big.NewInt(0).Div(f.Numerator, f.Denominator), 0)

return number.DecimalFormat(d, f.opts)
}
14 changes: 5 additions & 9 deletions entities/percent.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"math/big"

"github.com/miraclesu/uniswap-sdk-go/constants"
"github.com/miraclesu/uniswap-sdk-go/number"
)

var (
Expand All @@ -20,15 +21,10 @@ func NewPercent(num, deno *big.Int) *Percent {
}
}

// NOTE: format, rounding
// TODO
func (p *Percent) ToSignificant(significantDigits uint) string {
p.Multiply(Percent100)
return p.Fraction.ToSignificant(significantDigits)
func (p *Percent) ToSignificant(significantDigits uint, opt ...number.Option) string {
return p.Multiply(Percent100).ToSignificant(significantDigits, opt...)
}

// TODO
func (p *Percent) ToFixed(decimalPlaces uint) string {
p.Multiply(Percent100)
return p.Fraction.ToFixed(decimalPlaces)
func (p *Percent) ToFixed(decimalPlaces uint, opt ...number.Option) string {
return p.Multiply(Percent100).ToFixed(decimalPlaces, opt...)
}
9 changes: 5 additions & 4 deletions entities/price.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/ethereum/go-ethereum/common/math"

"github.com/miraclesu/uniswap-sdk-go/constants"
"github.com/miraclesu/uniswap-sdk-go/number"
)

type Price struct {
Expand Down Expand Up @@ -81,10 +82,10 @@ func (p *Price) Quote(currencyAmount *CurrencyAmount) (*CurrencyAmount, error) {
return NewEther(p.Fraction.Multiply(NewFraction(currencyAmount.Raw(), nil)).Quotient())
}

func (p *Price) ToSignificant(significantDigits uint) string {
return p.Adjusted().ToSignificant(significantDigits)
func (p *Price) ToSignificant(significantDigits uint, opt ...number.Option) string {
return p.Adjusted().ToSignificant(significantDigits, opt...)
}

func (p *Price) ToFixed(decimalPlaces uint) string {
return p.Adjusted().ToFixed(decimalPlaces)
func (p *Price) ToFixed(decimalPlaces uint, opt ...number.Option) string {
return p.Adjusted().ToFixed(decimalPlaces, opt...)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ go 1.15
require (
github.com/ethereum/go-ethereum v1.9.25
github.com/shopspring/decimal v1.2.0
github.com/wadey/go-rounding v1.1.0
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/wadey/go-rounding v1.1.0 h1:RAs9dMkB/uUHFv9ljlbRFC8/kBrQ5jhwt1GQq+2cciY=
github.com/wadey/go-rounding v1.1.0/go.mod h1:/uD953tCL6Fea2Yp+LZBBp8d60QSObkMJxY6SPOJ5QE=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand Down
129 changes: 129 additions & 0 deletions number/number.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package number

import (
"bytes"
"fmt"
"strings"

"github.com/shopspring/decimal"
)

func New(opt ...Option) *Options {
opts := *defaultOptions

for _, o := range opt {
o.apply(&opts)
}

return &opts
}

// DecimalFormat produces a string form of the given decimal.Decimal in base 10
//
// ref: https://github.com/dustin/go-humanize/blob/master/commaf.go#L13
func DecimalFormat(d decimal.Decimal, opts *Options) (formatted string) {
defer func() {
formatted = removeNonBreakingSpace(formatted)
}()

buf := &bytes.Buffer{}
s := d.String()
parts := strings.Split(s, ".")
if opts.decimalPlaces != nil && len(parts) > 1 && *opts.decimalPlaces < uint(len(parts[1])) {
s = d.StringFixed(int32(*opts.decimalPlaces))
}

if d.Sign() < 0 {
s = s[1:]
buf.WriteByte('-')
d.Abs()
}

parts = strings.Split(s, ".")
buf.WriteString(formatGroup(parts[0], opts.groupSize, opts.secondaryGroupSize, opts.groupSeparator))
if len(parts) == 1 && (opts.decimalPlaces == nil || *opts.decimalPlaces == 0) {
return buf.String()
}

if opts.decimalPlaces != nil {
if len(parts) == 1 {
parts = append(parts, "")
}

if size := *opts.decimalPlaces - uint(len(parts[1])); size > 0 {
parts[1] += fmt.Sprintf("%0*d", size, 0)
}

parts[1] = parts[1][:*opts.decimalPlaces]
}

buf.WriteByte(opts.decimalSeparator)
buf.WriteString(formatFraction(parts[1], opts.fractionGroupSize, opts.fractionGroupSeparator))

return strings.TrimRight(buf.String(), string(opts.fractionGroupSeparator))
}

func formatGroup(num string, groupSize, secondaryGroupSize uint, groupSeparator byte) string {
var buf = new(bytes.Buffer)
var pos uint = 0
iLen := uint(len(num))
if groupSize > 1 {
if groupSize < iLen {
iLen -= groupSize
if secondaryGroupSize > 0 {
groupSize = secondaryGroupSize
}

if subPOS := iLen % groupSize; subPOS != 0 {
pos += subPOS
buf.WriteString(num[:pos])
buf.WriteByte(groupSeparator)
}

for ; pos < iLen; pos += groupSize {
buf.WriteString(num[pos : pos+groupSize])
buf.WriteByte(groupSeparator)
}

buf.WriteString(num[iLen:])
} else {
buf.WriteString(num)
}
}

return buf.String()
}

func formatFraction(num string, fractionGroupSize uint, fractionGroupSeparator byte) string {
var buf = new(bytes.Buffer)
var pos uint = 0
fLen := uint(len(num))
if fractionGroupSize == 0 {
buf.WriteString(num)
return buf.String()
}

lastPOS := fLen % fractionGroupSize
for ; pos < fLen-lastPOS; pos += fractionGroupSize {
buf.WriteString(num[pos : pos+fractionGroupSize])
buf.WriteByte(fractionGroupSeparator)
}

if lastPOS > 0 {
buf.WriteString(num[pos : pos+lastPOS])
buf.WriteByte(fractionGroupSeparator)
}

return buf.String()
}

// DecimalRound sets d to its value rounded to the given precision using the given rounding mode.
//
// Returns d, which was modified in place.
func DecimalRound(d decimal.Decimal, opts *Options) (decimal.Decimal, error) {
if !opts.mode.Valid() {
return decimal.Decimal{}, ErrInvalidRM
}

return modeHandles[opts.mode](d, opts.prec)
}
Loading

0 comments on commit 8bbfadb

Please sign in to comment.