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