cosmos-sdk/x/tx/textual/dec.go
Facundo Medica 74ff94fdd5
fix: Format decimals correctly for signmode textual (#15129)
Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
2023-02-27 12:34:20 +00:00

77 lines
1.7 KiB
Go

package textual
import (
"context"
"fmt"
"math/big"
"strings"
"google.golang.org/protobuf/reflect/protoreflect"
"cosmossdk.io/math"
)
// NewDecValueRenderer returns a ValueRenderer for encoding sdk.Dec cosmos
// scalars.
func NewDecValueRenderer() ValueRenderer {
return decValueRenderer{}
}
type decValueRenderer struct{}
var _ ValueRenderer = decValueRenderer{}
func (vr decValueRenderer) Format(_ context.Context, v protoreflect.Value) ([]Screen, error) {
decStr := v.String()
// If the decimal doesn't contain a point, we assume it's a value formatted using the legacy
// `sdk.Dec`. So we try to parse it as an integer and then convert it to a
// decimal.
if !strings.Contains(decStr, ".") {
parsedInt, ok := new(big.Int).SetString(decStr, 10)
if !ok {
return nil, fmt.Errorf("invalid decimal: %s", decStr)
}
// We assume the decimal has 18 digits of precision.
decStr = math.LegacyNewDecFromBigIntWithPrec(parsedInt, math.LegacyPrecision).String()
}
formatted, err := math.FormatDec(decStr)
if err != nil {
return nil, err
}
return []Screen{{Content: formatted}}, nil
}
func (vr decValueRenderer) Parse(_ context.Context, screens []Screen) (protoreflect.Value, error) {
if n := len(screens); n != 1 {
return nilValue, fmt.Errorf("expected 1 screen, got: %d", n)
}
parsed, err := parseDec(screens[0].Content)
if err != nil {
return nilValue, err
}
return protoreflect.ValueOfString(parsed), nil
}
func parseDec(v string) (string, error) {
parts := strings.Split(v, ".")
if len(parts) > 2 {
return "", fmt.Errorf("invalid decimal: too many points in %s", v)
}
intPart, err := parseInt(parts[0])
if err != nil {
return "", err
}
if len(parts) == 1 {
return intPart, nil
}
return intPart + "." + parts[1], nil
}