cosmos-sdk/core/coins/format.go
atheeshp 9d12a5fce3
feat: implement parse method for Int,Dec,Coin (#13696)
* wip: parse coins

* review changes

* update tests

* update tests

* Use ValueOfString

* fix tests

* fix coin tests

* FIXES

* Add empty coins test case

* fix tests

* fix tests

* review changes

* fix tests

* fix tests

* revert format.go

* revert `format.go`

* review changes

* fix tests & review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* review changes

* proposal shorter code

* review changes

* conflicts

* review changes

* wip

* ParseRepeated for coins

* Revert listpb

* remove duplicate case

* revert some more

Co-authored-by: Amaury M <1293565+amaurym@users.noreply.github.com>
Co-authored-by: Marko <marbar3778@yahoo.com>
2022-12-02 14:26:25 +01:00

97 lines
2.7 KiB
Go

package coins
import (
"fmt"
"sort"
"strings"
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
"cosmossdk.io/math"
)
const emptyCoins = "zero"
// formatCoin formats a sdk.Coin into a value-rendered string, using the
// given metadata about the denom. It returns the formatted coin string, the
// display denom, and an optional error.
func formatCoin(coin *basev1beta1.Coin, metadata *bankv1beta1.Metadata) (string, error) {
coinDenom := coin.Denom
// Return early if no display denom or display denom is the current coin denom.
if metadata == nil || metadata.Display == "" || coinDenom == metadata.Display {
vr, err := math.FormatDec(coin.Amount)
return vr + " " + coin.Denom, err
}
dispDenom := metadata.Display
// Find exponents of both denoms.
var coinExp, dispExp uint32
foundCoinExp, foundDispExp := false, false
for _, unit := range metadata.DenomUnits {
if coinDenom == unit.Denom {
coinExp = unit.Exponent
foundCoinExp = true
}
if dispDenom == unit.Denom {
dispExp = unit.Exponent
foundDispExp = true
}
}
// If we didn't find either exponent, then we return early.
if !foundCoinExp || !foundDispExp {
vr, err := math.FormatInt(coin.Amount)
return vr + " " + coin.Denom, err
}
dispAmount, err := math.LegacyNewDecFromStr(coin.Amount)
if err != nil {
return "", err
}
if coinExp > dispExp {
dispAmount = dispAmount.Mul(math.LegacyNewDec(10).Power(uint64(coinExp - dispExp)))
} else {
dispAmount = dispAmount.Quo(math.LegacyNewDec(10).Power(uint64(dispExp - coinExp)))
}
vr, err := math.FormatDec(dispAmount.String())
return vr + " " + dispDenom, err
}
// formatCoins formats Coins into a value-rendered string, which uses
// `formatCoin` separated by ", " (a comma and a space), and sorted
// alphabetically by value-rendered denoms. It expects an array of metadata
// (optionally nil), where each metadata at index `i` MUST match the coin denom
// at the same index.
func FormatCoins(coins []*basev1beta1.Coin, metadata []*bankv1beta1.Metadata) (string, error) {
if len(coins) != len(metadata) {
return "", fmt.Errorf("formatCoins expect one metadata for each coin; expected %d, got %d", len(coins), len(metadata))
}
formatted := make([]string, len(coins))
for i, coin := range coins {
var err error
formatted[i], err = formatCoin(coin, metadata[i])
if err != nil {
return "", err
}
}
if len(coins) == 0 {
return emptyCoins, nil
}
// Sort the formatted coins by display denom.
sort.SliceStable(formatted, func(i, j int) bool {
denomI := strings.Split(formatted[i], " ")[1]
denomJ := strings.Split(formatted[j], " ")[1]
return denomI < denomJ
})
return strings.Join(formatted, ", "), nil
}