github.com/Finschia/finschia-sdk@v0.48.1/types/dec_coin.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  // ----------------------------------------------------------------------------
    12  // Decimal Coin
    13  
    14  // NewDecCoin creates a new DecCoin instance from an Int.
    15  func NewDecCoin(denom string, amount Int) DecCoin {
    16  	coin := NewCoin(denom, amount)
    17  
    18  	return DecCoin{
    19  		Denom:  coin.Denom,
    20  		Amount: coin.Amount.ToDec(),
    21  	}
    22  }
    23  
    24  // NewDecCoinFromDec creates a new DecCoin instance from a Dec.
    25  func NewDecCoinFromDec(denom string, amount Dec) DecCoin {
    26  	mustValidateDenom(denom)
    27  
    28  	if amount.IsNegative() {
    29  		panic(fmt.Sprintf("negative decimal coin amount: %v\n", amount))
    30  	}
    31  
    32  	return DecCoin{
    33  		Denom:  denom,
    34  		Amount: amount,
    35  	}
    36  }
    37  
    38  // NewDecCoinFromCoin creates a new DecCoin from a Coin.
    39  func NewDecCoinFromCoin(coin Coin) DecCoin {
    40  	if err := coin.Validate(); err != nil {
    41  		panic(err)
    42  	}
    43  
    44  	return DecCoin{
    45  		Denom:  coin.Denom,
    46  		Amount: coin.Amount.ToDec(),
    47  	}
    48  }
    49  
    50  // NewInt64DecCoin returns a new DecCoin with a denomination and amount. It will
    51  // panic if the amount is negative or denom is invalid.
    52  func NewInt64DecCoin(denom string, amount int64) DecCoin {
    53  	return NewDecCoin(denom, NewInt(amount))
    54  }
    55  
    56  // IsZero returns if the DecCoin amount is zero.
    57  func (coin DecCoin) IsZero() bool {
    58  	return coin.Amount.IsZero()
    59  }
    60  
    61  // IsGTE returns true if they are the same type and the receiver is
    62  // an equal or greater value.
    63  func (coin DecCoin) IsGTE(other DecCoin) bool {
    64  	if coin.Denom != other.Denom {
    65  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    66  	}
    67  
    68  	return !coin.Amount.LT(other.Amount)
    69  }
    70  
    71  // IsLT returns true if they are the same type and the receiver is
    72  // a smaller value.
    73  func (coin DecCoin) IsLT(other DecCoin) bool {
    74  	if coin.Denom != other.Denom {
    75  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    76  	}
    77  
    78  	return coin.Amount.LT(other.Amount)
    79  }
    80  
    81  // IsEqual returns true if the two sets of Coins have the same value.
    82  func (coin DecCoin) IsEqual(other DecCoin) bool {
    83  	if coin.Denom != other.Denom {
    84  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    85  	}
    86  
    87  	return coin.Amount.Equal(other.Amount)
    88  }
    89  
    90  // Add adds amounts of two decimal coins with same denom.
    91  func (coin DecCoin) Add(coinB DecCoin) DecCoin {
    92  	if coin.Denom != coinB.Denom {
    93  		panic(fmt.Sprintf("coin denom different: %v %v\n", coin.Denom, coinB.Denom))
    94  	}
    95  	return DecCoin{coin.Denom, coin.Amount.Add(coinB.Amount)}
    96  }
    97  
    98  // Sub subtracts amounts of two decimal coins with same denom.
    99  func (coin DecCoin) Sub(coinB DecCoin) DecCoin {
   100  	if coin.Denom != coinB.Denom {
   101  		panic(fmt.Sprintf("coin denom different: %v %v\n", coin.Denom, coinB.Denom))
   102  	}
   103  	res := DecCoin{coin.Denom, coin.Amount.Sub(coinB.Amount)}
   104  	if res.IsNegative() {
   105  		panic("negative decimal coin amount")
   106  	}
   107  	return res
   108  }
   109  
   110  // TruncateDecimal returns a Coin with a truncated decimal and a DecCoin for the
   111  // change. Note, the change may be zero.
   112  func (coin DecCoin) TruncateDecimal() (Coin, DecCoin) {
   113  	truncated := coin.Amount.TruncateInt()
   114  	change := coin.Amount.Sub(truncated.ToDec())
   115  	return NewCoin(coin.Denom, truncated), NewDecCoinFromDec(coin.Denom, change)
   116  }
   117  
   118  // IsPositive returns true if coin amount is positive.
   119  //
   120  // TODO: Remove once unsigned integers are used.
   121  func (coin DecCoin) IsPositive() bool {
   122  	return coin.Amount.IsPositive()
   123  }
   124  
   125  // IsNegative returns true if the coin amount is negative and false otherwise.
   126  //
   127  // TODO: Remove once unsigned integers are used.
   128  func (coin DecCoin) IsNegative() bool {
   129  	return coin.Amount.IsNegative()
   130  }
   131  
   132  // String implements the Stringer interface for DecCoin. It returns a
   133  // human-readable representation of a decimal coin.
   134  func (coin DecCoin) String() string {
   135  	return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
   136  }
   137  
   138  // Validate returns an error if the DecCoin has a negative amount or if the denom is invalid.
   139  func (coin DecCoin) Validate() error {
   140  	if err := ValidateDenom(coin.Denom); err != nil {
   141  		return err
   142  	}
   143  	if coin.IsNegative() {
   144  		return fmt.Errorf("decimal coin %s amount cannot be negative", coin)
   145  	}
   146  	return nil
   147  }
   148  
   149  // IsValid returns true if the DecCoin has a non-negative amount and the denom is valid.
   150  func (coin DecCoin) IsValid() bool {
   151  	return coin.Validate() == nil
   152  }
   153  
   154  // ----------------------------------------------------------------------------
   155  // Decimal Coins
   156  
   157  // DecCoins defines a slice of coins with decimal values
   158  type DecCoins []DecCoin
   159  
   160  // NewDecCoins constructs a new coin set with with decimal values
   161  // from DecCoins. The provided coins will be sanitized by removing
   162  // zero coins and sorting the coin set. A panic will occur if the coin set is not valid.
   163  func NewDecCoins(decCoins ...DecCoin) DecCoins {
   164  	newDecCoins := sanitizeDecCoins(decCoins)
   165  	if err := newDecCoins.Validate(); err != nil {
   166  		panic(fmt.Errorf("invalid coin set %s: %w", newDecCoins, err))
   167  	}
   168  
   169  	return newDecCoins
   170  }
   171  
   172  func sanitizeDecCoins(decCoins []DecCoin) DecCoins {
   173  	// remove zeroes
   174  	newDecCoins := removeZeroDecCoins(decCoins)
   175  	if len(newDecCoins) == 0 {
   176  		return DecCoins{}
   177  	}
   178  
   179  	return newDecCoins.Sort()
   180  }
   181  
   182  // NewDecCoinsFromCoins constructs a new coin set with decimal values
   183  // from regular Coins.
   184  func NewDecCoinsFromCoins(coins ...Coin) DecCoins {
   185  	decCoins := make(DecCoins, len(coins))
   186  	newCoins := NewCoins(coins...)
   187  	for i, coin := range newCoins {
   188  		decCoins[i] = NewDecCoinFromCoin(coin)
   189  	}
   190  
   191  	return decCoins
   192  }
   193  
   194  // String implements the Stringer interface for DecCoins. It returns a
   195  // human-readable representation of decimal coins.
   196  func (coins DecCoins) String() string {
   197  	if len(coins) == 0 {
   198  		return ""
   199  	}
   200  
   201  	out := ""
   202  	for _, coin := range coins {
   203  		out += fmt.Sprintf("%v,", coin.String())
   204  	}
   205  
   206  	return out[:len(out)-1]
   207  }
   208  
   209  // TruncateDecimal returns the coins with truncated decimals and returns the
   210  // change. Note, it will not return any zero-amount coins in either the truncated or
   211  // change coins.
   212  func (coins DecCoins) TruncateDecimal() (truncatedCoins Coins, changeCoins DecCoins) {
   213  	for _, coin := range coins {
   214  		truncated, change := coin.TruncateDecimal()
   215  		if !truncated.IsZero() {
   216  			truncatedCoins = truncatedCoins.Add(truncated)
   217  		}
   218  		if !change.IsZero() {
   219  			changeCoins = changeCoins.Add(change)
   220  		}
   221  	}
   222  
   223  	return truncatedCoins, changeCoins
   224  }
   225  
   226  // Add adds two sets of DecCoins.
   227  //
   228  // NOTE: Add operates under the invariant that coins are sorted by
   229  // denominations.
   230  //
   231  // CONTRACT: Add will never return Coins where one Coin has a non-positive
   232  // amount. In otherwords, IsValid will always return true.
   233  func (coins DecCoins) Add(coinsB ...DecCoin) DecCoins {
   234  	return coins.safeAdd(coinsB)
   235  }
   236  
   237  // safeAdd will perform addition of two DecCoins sets. If both coin sets are
   238  // empty, then an empty set is returned. If only a single set is empty, the
   239  // other set is returned. Otherwise, the coins are compared in order of their
   240  // denomination and addition only occurs when the denominations match, otherwise
   241  // the coin is simply added to the sum assuming it's not zero.
   242  func (coins DecCoins) safeAdd(coinsB DecCoins) DecCoins {
   243  	sum := ([]DecCoin)(nil)
   244  	indexA, indexB := 0, 0
   245  	lenA, lenB := len(coins), len(coinsB)
   246  
   247  	for {
   248  		if indexA == lenA {
   249  			if indexB == lenB {
   250  				// return nil coins if both sets are empty
   251  				return sum
   252  			}
   253  
   254  			// return set B (excluding zero coins) if set A is empty
   255  			return append(sum, removeZeroDecCoins(coinsB[indexB:])...)
   256  		} else if indexB == lenB {
   257  			// return set A (excluding zero coins) if set B is empty
   258  			return append(sum, removeZeroDecCoins(coins[indexA:])...)
   259  		}
   260  
   261  		coinA, coinB := coins[indexA], coinsB[indexB]
   262  
   263  		switch strings.Compare(coinA.Denom, coinB.Denom) {
   264  		case -1: // coin A denom < coin B denom
   265  			if !coinA.IsZero() {
   266  				sum = append(sum, coinA)
   267  			}
   268  
   269  			indexA++
   270  
   271  		case 0: // coin A denom == coin B denom
   272  			res := coinA.Add(coinB)
   273  			if !res.IsZero() {
   274  				sum = append(sum, res)
   275  			}
   276  
   277  			indexA++
   278  			indexB++
   279  
   280  		case 1: // coin A denom > coin B denom
   281  			if !coinB.IsZero() {
   282  				sum = append(sum, coinB)
   283  			}
   284  
   285  			indexB++
   286  		}
   287  	}
   288  }
   289  
   290  // negative returns a set of coins with all amount negative.
   291  func (coins DecCoins) negative() DecCoins {
   292  	res := make([]DecCoin, 0, len(coins))
   293  	for _, coin := range coins {
   294  		res = append(res, DecCoin{
   295  			Denom:  coin.Denom,
   296  			Amount: coin.Amount.Neg(),
   297  		})
   298  	}
   299  	return res
   300  }
   301  
   302  // Sub subtracts a set of DecCoins from another (adds the inverse).
   303  func (coins DecCoins) Sub(coinsB DecCoins) DecCoins {
   304  	diff, hasNeg := coins.SafeSub(coinsB)
   305  	if hasNeg {
   306  		panic("negative coin amount")
   307  	}
   308  
   309  	return diff
   310  }
   311  
   312  // SafeSub performs the same arithmetic as Sub but returns a boolean if any
   313  // negative coin amount was returned.
   314  func (coins DecCoins) SafeSub(coinsB DecCoins) (DecCoins, bool) {
   315  	diff := coins.safeAdd(coinsB.negative())
   316  	return diff, diff.IsAnyNegative()
   317  }
   318  
   319  // Intersect will return a new set of coins which contains the minimum DecCoin
   320  // for common denoms found in both `coins` and `coinsB`. For denoms not common
   321  // to both `coins` and `coinsB` the minimum is considered to be 0, thus they
   322  // are not added to the final set. In other words, trim any denom amount from
   323  // coin which exceeds that of coinB, such that (coin.Intersect(coinB)).IsLTE(coinB).
   324  // See also Coins.Min().
   325  func (coins DecCoins) Intersect(coinsB DecCoins) DecCoins {
   326  	res := make([]DecCoin, len(coins))
   327  	for i, coin := range coins {
   328  		minCoin := DecCoin{
   329  			Denom:  coin.Denom,
   330  			Amount: MinDec(coin.Amount, coinsB.AmountOf(coin.Denom)),
   331  		}
   332  		res[i] = minCoin
   333  	}
   334  	return removeZeroDecCoins(res)
   335  }
   336  
   337  // GetDenomByIndex returns the Denom to make the findDup generic
   338  func (coins DecCoins) GetDenomByIndex(i int) string {
   339  	return coins[i].Denom
   340  }
   341  
   342  // IsAnyNegative returns true if there is at least one coin whose amount
   343  // is negative; returns false otherwise. It returns false if the DecCoins set
   344  // is empty too.
   345  //
   346  // TODO: Remove once unsigned integers are used.
   347  func (coins DecCoins) IsAnyNegative() bool {
   348  	for _, coin := range coins {
   349  		if coin.IsNegative() {
   350  			return true
   351  		}
   352  	}
   353  
   354  	return false
   355  }
   356  
   357  // MulDec multiplies all the coins by a decimal.
   358  //
   359  // CONTRACT: No zero coins will be returned.
   360  func (coins DecCoins) MulDec(d Dec) DecCoins {
   361  	var res DecCoins
   362  	for _, coin := range coins {
   363  		product := DecCoin{
   364  			Denom:  coin.Denom,
   365  			Amount: coin.Amount.Mul(d),
   366  		}
   367  
   368  		if !product.IsZero() {
   369  			res = res.Add(product)
   370  		}
   371  	}
   372  
   373  	return res
   374  }
   375  
   376  // MulDecTruncate multiplies all the decimal coins by a decimal, truncating. It
   377  // panics if d is zero.
   378  //
   379  // CONTRACT: No zero coins will be returned.
   380  func (coins DecCoins) MulDecTruncate(d Dec) DecCoins {
   381  	var res DecCoins
   382  
   383  	for _, coin := range coins {
   384  		product := DecCoin{
   385  			Denom:  coin.Denom,
   386  			Amount: coin.Amount.MulTruncate(d),
   387  		}
   388  
   389  		if !product.IsZero() {
   390  			res = res.Add(product)
   391  		}
   392  	}
   393  
   394  	return res
   395  }
   396  
   397  // QuoDec divides all the decimal coins by a decimal. It panics if d is zero.
   398  //
   399  // CONTRACT: No zero coins will be returned.
   400  func (coins DecCoins) QuoDec(d Dec) DecCoins {
   401  	if d.IsZero() {
   402  		panic("invalid zero decimal")
   403  	}
   404  
   405  	var res DecCoins
   406  	for _, coin := range coins {
   407  		quotient := DecCoin{
   408  			Denom:  coin.Denom,
   409  			Amount: coin.Amount.Quo(d),
   410  		}
   411  
   412  		if !quotient.IsZero() {
   413  			res = res.Add(quotient)
   414  		}
   415  	}
   416  
   417  	return res
   418  }
   419  
   420  // QuoDecTruncate divides all the decimal coins by a decimal, truncating. It
   421  // panics if d is zero.
   422  //
   423  // CONTRACT: No zero coins will be returned.
   424  func (coins DecCoins) QuoDecTruncate(d Dec) DecCoins {
   425  	if d.IsZero() {
   426  		panic("invalid zero decimal")
   427  	}
   428  
   429  	var res DecCoins
   430  	for _, coin := range coins {
   431  		quotient := DecCoin{
   432  			Denom:  coin.Denom,
   433  			Amount: coin.Amount.QuoTruncate(d),
   434  		}
   435  
   436  		if !quotient.IsZero() {
   437  			res = res.Add(quotient)
   438  		}
   439  	}
   440  
   441  	return res
   442  }
   443  
   444  // Empty returns true if there are no coins and false otherwise.
   445  func (coins DecCoins) Empty() bool {
   446  	return len(coins) == 0
   447  }
   448  
   449  // AmountOf returns the amount of a denom from deccoins
   450  func (coins DecCoins) AmountOf(denom string) Dec {
   451  	mustValidateDenom(denom)
   452  
   453  	switch len(coins) {
   454  	case 0:
   455  		return ZeroDec()
   456  
   457  	case 1:
   458  		coin := coins[0]
   459  		if coin.Denom == denom {
   460  			return coin.Amount
   461  		}
   462  		return ZeroDec()
   463  
   464  	default:
   465  		midIdx := len(coins) / 2 // 2:1, 3:1, 4:2
   466  		coin := coins[midIdx]
   467  
   468  		switch {
   469  		case denom < coin.Denom:
   470  			return coins[:midIdx].AmountOf(denom)
   471  		case denom == coin.Denom:
   472  			return coin.Amount
   473  		default:
   474  			return coins[midIdx+1:].AmountOf(denom)
   475  		}
   476  	}
   477  }
   478  
   479  // IsEqual returns true if the two sets of DecCoins have the same value.
   480  func (coins DecCoins) IsEqual(coinsB DecCoins) bool {
   481  	if len(coins) != len(coinsB) {
   482  		return false
   483  	}
   484  
   485  	coins = coins.Sort()
   486  	coinsB = coinsB.Sort()
   487  
   488  	for i := 0; i < len(coins); i++ {
   489  		if !coins[i].IsEqual(coinsB[i]) {
   490  			return false
   491  		}
   492  	}
   493  
   494  	return true
   495  }
   496  
   497  // IsZero returns whether all coins are zero
   498  func (coins DecCoins) IsZero() bool {
   499  	for _, coin := range coins {
   500  		if !coin.Amount.IsZero() {
   501  			return false
   502  		}
   503  	}
   504  	return true
   505  }
   506  
   507  // Validate checks that the DecCoins are sorted, have positive amount, with a valid and unique
   508  // denomination (i.e no duplicates). Otherwise, it returns an error.
   509  func (coins DecCoins) Validate() error {
   510  	switch len(coins) {
   511  	case 0:
   512  		return nil
   513  
   514  	case 1:
   515  		if err := ValidateDenom(coins[0].Denom); err != nil {
   516  			return err
   517  		}
   518  		if !coins[0].IsPositive() {
   519  			return fmt.Errorf("coin %s amount is not positive", coins[0])
   520  		}
   521  		return nil
   522  	default:
   523  		// check single coin case
   524  		if err := (DecCoins{coins[0]}).Validate(); err != nil {
   525  			return err
   526  		}
   527  
   528  		lowDenom := coins[0].Denom
   529  		seenDenoms := make(map[string]bool)
   530  		seenDenoms[lowDenom] = true
   531  
   532  		for _, coin := range coins[1:] {
   533  			if seenDenoms[coin.Denom] {
   534  				return fmt.Errorf("duplicate denomination %s", coin.Denom)
   535  			}
   536  			if err := ValidateDenom(coin.Denom); err != nil {
   537  				return err
   538  			}
   539  			if coin.Denom <= lowDenom {
   540  				return fmt.Errorf("denomination %s is not sorted", coin.Denom)
   541  			}
   542  			if !coin.IsPositive() {
   543  				return fmt.Errorf("coin %s amount is not positive", coin.Denom)
   544  			}
   545  
   546  			// we compare each coin against the last denom
   547  			lowDenom = coin.Denom
   548  			seenDenoms[coin.Denom] = true
   549  		}
   550  
   551  		return nil
   552  	}
   553  }
   554  
   555  // IsValid calls Validate and returns true when the DecCoins are sorted, have positive amount, with a
   556  // valid and unique denomination (i.e no duplicates).
   557  func (coins DecCoins) IsValid() bool {
   558  	return coins.Validate() == nil
   559  }
   560  
   561  // IsAllPositive returns true if there is at least one coin and all currencies
   562  // have a positive value.
   563  //
   564  // TODO: Remove once unsigned integers are used.
   565  func (coins DecCoins) IsAllPositive() bool {
   566  	if len(coins) == 0 {
   567  		return false
   568  	}
   569  
   570  	for _, coin := range coins {
   571  		if !coin.IsPositive() {
   572  			return false
   573  		}
   574  	}
   575  
   576  	return true
   577  }
   578  
   579  func removeZeroDecCoins(coins DecCoins) DecCoins {
   580  	result := make([]DecCoin, 0, len(coins))
   581  
   582  	for _, coin := range coins {
   583  		if !coin.IsZero() {
   584  			result = append(result, coin)
   585  		}
   586  	}
   587  
   588  	return result
   589  }
   590  
   591  //-----------------------------------------------------------------------------
   592  // Sorting
   593  
   594  var _ sort.Interface = DecCoins{}
   595  
   596  // Len implements sort.Interface for DecCoins
   597  func (coins DecCoins) Len() int { return len(coins) }
   598  
   599  // Less implements sort.Interface for DecCoins
   600  func (coins DecCoins) Less(i, j int) bool { return coins[i].Denom < coins[j].Denom }
   601  
   602  // Swap implements sort.Interface for DecCoins
   603  func (coins DecCoins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] }
   604  
   605  // Sort is a helper function to sort the set of decimal coins in-place.
   606  func (coins DecCoins) Sort() DecCoins {
   607  	sort.Sort(coins)
   608  	return coins
   609  }
   610  
   611  // ----------------------------------------------------------------------------
   612  // Parsing
   613  
   614  // ParseDecCoin parses a decimal coin from a string, returning an error if
   615  // invalid. An empty string is considered invalid.
   616  func ParseDecCoin(coinStr string) (coin DecCoin, err error) {
   617  	coinStr = strings.TrimSpace(coinStr)
   618  
   619  	matches := reDecCoin.FindStringSubmatch(coinStr)
   620  	if matches == nil {
   621  		return DecCoin{}, fmt.Errorf("invalid decimal coin expression: %s", coinStr)
   622  	}
   623  
   624  	amountStr, denomStr := matches[1], matches[2]
   625  
   626  	amount, err := NewDecFromStr(amountStr)
   627  	if err != nil {
   628  		return DecCoin{}, errors.Wrap(err, fmt.Sprintf("failed to parse decimal coin amount: %s", amountStr))
   629  	}
   630  
   631  	if err := ValidateDenom(denomStr); err != nil {
   632  		return DecCoin{}, fmt.Errorf("invalid denom cannot contain upper case characters or spaces: %s", err)
   633  	}
   634  
   635  	return NewDecCoinFromDec(denomStr, amount), nil
   636  }
   637  
   638  // ParseDecCoins will parse out a list of decimal coins separated by commas. If the parsing is successuful,
   639  // the provided coins will be sanitized by removing zero coins and sorting the coin set. Lastly
   640  // a validation of the coin set is executed. If the check passes, ParseDecCoins will return the sanitized coins.
   641  // Otherwise it will return an error.
   642  // If an empty string is provided to ParseDecCoins, it returns nil Coins.
   643  // Expected format: "{amount0}{denomination},...,{amountN}{denominationN}"
   644  func ParseDecCoins(coinsStr string) (DecCoins, error) {
   645  	coinsStr = strings.TrimSpace(coinsStr)
   646  	if len(coinsStr) == 0 {
   647  		return nil, nil
   648  	}
   649  
   650  	coinStrs := strings.Split(coinsStr, ",")
   651  	decCoins := make(DecCoins, len(coinStrs))
   652  	for i, coinStr := range coinStrs {
   653  		coin, err := ParseDecCoin(coinStr)
   654  		if err != nil {
   655  			return nil, err
   656  		}
   657  
   658  		decCoins[i] = coin
   659  	}
   660  
   661  	newDecCoins := sanitizeDecCoins(decCoins)
   662  	if err := newDecCoins.Validate(); err != nil {
   663  		return nil, err
   664  	}
   665  
   666  	return newDecCoins, nil
   667  }