github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/types/ibc_adapter.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"strings"
     7  )
     8  
     9  // String provides a human-readable representation of a coin
    10  func (coin CoinAdapter) String() string {
    11  	return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
    12  }
    13  
    14  // Validate returns an error if the Coin has a negative amount or if
    15  // the denom is invalid.
    16  func (coin CoinAdapter) Validate() error {
    17  	if err := ValidateDenom(coin.Denom); err != nil {
    18  		return err
    19  	}
    20  
    21  	if coin.Amount.IsNegative() {
    22  		return fmt.Errorf("negative coin amount: %v", coin.Amount)
    23  	}
    24  
    25  	return nil
    26  }
    27  
    28  // IsValid returns true if the Coin has a non-negative amount and the denom is valid.
    29  func (coin CoinAdapter) IsValid() bool {
    30  	return coin.Validate() == nil
    31  }
    32  
    33  // IsZero returns if this represents no money
    34  func (coin CoinAdapter) IsZero() bool {
    35  	return coin.Amount.IsZero()
    36  }
    37  
    38  // IsGTE returns true if they are the same type and the receiver is
    39  // an equal or greater value
    40  func (coin CoinAdapter) IsGTE(other CoinAdapter) bool {
    41  	if coin.Denom != other.Denom {
    42  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    43  	}
    44  
    45  	return !coin.Amount.LT(other.Amount)
    46  }
    47  
    48  // IsLT returns true if they are the same type and the receiver is
    49  // a smaller value
    50  func (coin CoinAdapter) IsLT(other CoinAdapter) bool {
    51  	if coin.Denom != other.Denom {
    52  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    53  	}
    54  
    55  	return coin.Amount.LT(other.Amount)
    56  }
    57  
    58  // IsEqual returns true if the two sets of Coins have the same value
    59  func (coin CoinAdapter) IsEqual(other CoinAdapter) bool {
    60  	if coin.Denom != other.Denom {
    61  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
    62  	}
    63  
    64  	return coin.Amount.Equal(other.Amount)
    65  }
    66  
    67  // Add adds amounts of two coins with same denom. If the coins differ in denom then
    68  // it panics.
    69  func (coin CoinAdapter) Add(coinB CoinAdapter) CoinAdapter {
    70  	if coin.Denom != coinB.Denom {
    71  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, coinB.Denom))
    72  	}
    73  
    74  	return CoinAdapter{coin.Denom, coin.Amount.Add(coinB.Amount)}
    75  }
    76  
    77  // Sub subtracts amounts of two coins with same denom. If the coins differ in denom
    78  // then it panics.
    79  func (coin CoinAdapter) Sub(coinB CoinAdapter) CoinAdapter {
    80  	if coin.Denom != coinB.Denom {
    81  		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, coinB.Denom))
    82  	}
    83  
    84  	res := CoinAdapter{coin.Denom, coin.Amount.Sub(coinB.Amount)}
    85  	if res.IsNegative() {
    86  		panic("negative coin amount")
    87  	}
    88  
    89  	return res
    90  }
    91  
    92  // IsPositive returns true if coin amount is positive.
    93  //
    94  // TODO: Remove once unsigned integers are used.
    95  func (coin CoinAdapter) IsPositive() bool {
    96  	return coin.Amount.Sign() == 1
    97  }
    98  
    99  // IsNegative returns true if the coin amount is negative and false otherwise.
   100  //
   101  // TODO: Remove once unsigned integers are used.
   102  func (coin CoinAdapter) IsNegative() bool {
   103  	return coin.Amount.Sign() == -1
   104  }
   105  
   106  // Unmarshal implements the gogo proto custom type interface.
   107  func (i *Int) Unmarshal(data []byte) error {
   108  	if len(data) == 0 {
   109  		i = nil
   110  		return nil
   111  	}
   112  
   113  	if i.i == nil {
   114  		i.i = new(big.Int)
   115  	}
   116  
   117  	if err := i.i.UnmarshalText(data); err != nil {
   118  		return err
   119  	}
   120  
   121  	if i.i.BitLen() > maxBitLen {
   122  		return fmt.Errorf("integer out of range; got: %d, max: %d", i.i.BitLen(), maxBitLen)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // Size implements the gogo proto custom type interface.
   129  func (i *Int) Size() int {
   130  	bz, _ := i.Marshal()
   131  	return len(bz)
   132  }
   133  
   134  // Marshal implements the gogo proto custom type interface.
   135  func (i Int) Marshal() ([]byte, error) {
   136  	if i.i == nil {
   137  		i.i = new(big.Int)
   138  	}
   139  	return i.i.MarshalText()
   140  }
   141  
   142  // MarshalTo implements the gogo proto custom type interface.
   143  func (i *Int) MarshalTo(data []byte) (n int, err error) {
   144  	if i.i == nil {
   145  		i.i = new(big.Int)
   146  	}
   147  	if len(i.i.Bytes()) == 0 {
   148  		copy(data, []byte{0x30})
   149  		return 1, nil
   150  	}
   151  
   152  	bz, err := i.Marshal()
   153  	if err != nil {
   154  		return 0, err
   155  	}
   156  
   157  	copy(data, bz)
   158  	return len(bz), nil
   159  }
   160  
   161  func NewIBCCoin(denom string, amount interface{}) DecCoin {
   162  	switch amount := amount.(type) {
   163  	case Int:
   164  		return NewIBCDecCoin(denom, amount)
   165  	case Dec:
   166  		return NewDecCoinFromDec(denom, amount)
   167  	default:
   168  		panic("Invalid amount")
   169  	}
   170  }
   171  
   172  func NewIBCDecCoin(denom string, amount Int) DecCoin {
   173  	if err := validateIBCCotin(denom, amount); err != nil {
   174  		panic(err)
   175  	}
   176  
   177  	return DecCoin{
   178  		Denom:  denom,
   179  		Amount: amount.ToDec(),
   180  	}
   181  }
   182  
   183  func validateIBCCotin(denom string, amount Int) error {
   184  	if !validIBCCoinDenom(denom) {
   185  		return fmt.Errorf("invalid denom: %s", denom)
   186  	}
   187  
   188  	if amount.IsNegative() {
   189  		return fmt.Errorf("negative coin amount: %v", amount)
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func validIBCCoinDenom(denom string) bool {
   196  	return ibcReDnm.MatchString(denom)
   197  }
   198  
   199  // NewCoins constructs a new coin set.
   200  func NewIBCCoins(coins ...Coin) Coins {
   201  	// remove zeroes
   202  	newCoins := removeZeroCoins(Coins(coins))
   203  	if len(newCoins) == 0 {
   204  		return Coins{}
   205  	}
   206  
   207  	newCoins.Sort()
   208  
   209  	// detect duplicate Denoms
   210  	if dupIndex := findDup(newCoins); dupIndex != -1 {
   211  		panic(fmt.Errorf("find duplicate denom: %s", newCoins[dupIndex]))
   212  	}
   213  	newCoins.IsValid()
   214  	if !ValidCoins(newCoins) {
   215  		panic(fmt.Errorf("invalid coin set: %s", newCoins))
   216  	}
   217  
   218  	return newCoins
   219  }
   220  func ValidCoins(coins Coins) bool {
   221  	switch len(coins) {
   222  	case 0:
   223  		return true
   224  
   225  	case 1:
   226  		if !validIBCCoinDenom(coins[0].Denom) {
   227  			return false
   228  		}
   229  		return coins[0].IsPositive()
   230  
   231  	default:
   232  		// check single coin case
   233  		if !ValidCoins(DecCoins{coins[0]}) {
   234  			return false
   235  		}
   236  
   237  		lowDenom := coins[0].Denom
   238  		for _, coin := range coins[1:] {
   239  			if strings.ToLower(coin.Denom) != coin.Denom {
   240  				return false
   241  			}
   242  			if coin.Denom <= lowDenom {
   243  				return false
   244  			}
   245  			if !coin.IsPositive() {
   246  				return false
   247  			}
   248  
   249  			// we compare each coin against the last denom
   250  			lowDenom = coin.Denom
   251  		}
   252  
   253  		return true
   254  	}
   255  }
   256  
   257  func (coin DecCoinAdapter) String() string {
   258  	return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
   259  }
   260  
   261  func (ip IntProtoAdapter) String() string {
   262  	return ip.Int.String()
   263  }
   264  
   265  func (dp DecProtoAdapter) String() string {
   266  	return dp.Dec.String()
   267  }