93 lines
2.7 KiB
Go
93 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"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
exponentDiff := int64(coinExp) - int64(dispExp)
|
|
|
|
dispAmount, err := math.LegacyNewDecFromStr(coin.Amount)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if exponentDiff > 0 {
|
|
dispAmount = dispAmount.Mul(math.LegacyNewDec(10).Power(uint64(exponentDiff)))
|
|
} else {
|
|
dispAmount = dispAmount.Quo(math.LegacyNewDec(10).Power(uint64(-exponentDiff)))
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|