github.com/Finschia/finschia-sdk@v0.48.1/x/collection/collection.go (about)

     1  package collection
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  
     8  	proto "github.com/gogo/protobuf/proto"
     9  
    10  	codectypes "github.com/Finschia/finschia-sdk/codec/types"
    11  	sdk "github.com/Finschia/finschia-sdk/types"
    12  )
    13  
    14  const (
    15  	prefixLegacyPermission = "LEGACY_PERMISSION_"
    16  )
    17  
    18  // Deprecated: use Permission.
    19  func LegacyPermissionFromString(name string) LegacyPermission {
    20  	legacyPermissionName := prefixLegacyPermission + strings.ToUpper(name)
    21  	return LegacyPermission(LegacyPermission_value[legacyPermissionName])
    22  }
    23  
    24  func (x LegacyPermission) String() string {
    25  	lenPrefix := len(prefixLegacyPermission)
    26  	return strings.ToLower(LegacyPermission_name[int32(x)][lenPrefix:])
    27  }
    28  
    29  func DefaultNextClassIDs(contractID string) NextClassIDs {
    30  	return NextClassIDs{
    31  		ContractId:  contractID,
    32  		Fungible:    sdk.NewUint(1),
    33  		NonFungible: sdk.NewUint(1 << 28).Incr(), // "10000000 + 1"
    34  	}
    35  }
    36  
    37  func validateParams(params Params) error {
    38  	// limits are uint32, so no need to validate them.
    39  	return nil
    40  }
    41  
    42  type TokenClass interface {
    43  	proto.Message
    44  
    45  	GetId() string
    46  	SetId(ids *NextClassIDs)
    47  
    48  	SetName(name string)
    49  
    50  	SetMeta(meta string)
    51  
    52  	ValidateBasic() error
    53  }
    54  
    55  func TokenClassToAny(class TokenClass) *codectypes.Any {
    56  	msg := class.(proto.Message)
    57  
    58  	any, err := codectypes.NewAnyWithValue(msg)
    59  	if err != nil {
    60  		panic(err)
    61  	}
    62  
    63  	return any
    64  }
    65  
    66  func TokenClassFromAny(any *codectypes.Any) TokenClass {
    67  	class := any.GetCachedValue().(TokenClass)
    68  	return class
    69  }
    70  
    71  func TokenClassUnpackInterfaces(any *codectypes.Any, unpacker codectypes.AnyUnpacker) error {
    72  	var class TokenClass
    73  	return unpacker.UnpackAny(any, &class)
    74  }
    75  
    76  // ----------------------------------------------------------------------------
    77  // FTClass
    78  var _ TokenClass = (*FTClass)(nil)
    79  
    80  //nolint:golint
    81  func (c *FTClass) SetId(ids *NextClassIDs) {
    82  	id := ids.Fungible
    83  	ids.Fungible = id.Incr()
    84  	c.Id = fmt.Sprintf("%08x", id.Uint64())
    85  }
    86  
    87  func (c *FTClass) SetName(name string) {
    88  	c.Name = name
    89  }
    90  
    91  func (c *FTClass) SetMeta(meta string) {
    92  	c.Meta = meta
    93  }
    94  
    95  func (c FTClass) ValidateBasic() error {
    96  	if err := ValidateClassID(c.Id); err != nil {
    97  		return err
    98  	}
    99  
   100  	if err := validateName(c.Name); err != nil {
   101  		return err
   102  	}
   103  	if err := validateMeta(c.Meta); err != nil {
   104  		return err
   105  	}
   106  	if err := validateDecimals(c.Decimals); err != nil {
   107  		return err
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // ----------------------------------------------------------------------------
   114  // NFTClass
   115  var _ TokenClass = (*NFTClass)(nil)
   116  
   117  //nolint:golint
   118  func (c *NFTClass) SetId(ids *NextClassIDs) {
   119  	id := ids.NonFungible
   120  	ids.NonFungible = id.Incr()
   121  	c.Id = fmt.Sprintf("%08x", id.Uint64())
   122  }
   123  
   124  func (c *NFTClass) SetName(name string) {
   125  	c.Name = name
   126  }
   127  
   128  func (c *NFTClass) SetMeta(meta string) {
   129  	c.Meta = meta
   130  }
   131  
   132  func (c NFTClass) ValidateBasic() error {
   133  	if err := ValidateClassID(c.Id); err != nil {
   134  		return err
   135  	}
   136  
   137  	if err := validateName(c.Name); err != nil {
   138  		return err
   139  	}
   140  	if err := validateMeta(c.Meta); err != nil {
   141  		return err
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // ----------------------------------------------------------------------------
   148  // Coin
   149  func NewFTCoin(classID string, amount sdk.Int) Coin {
   150  	return NewCoin(NewFTID(classID), amount)
   151  }
   152  
   153  func NewNFTCoin(classID string, number int) Coin {
   154  	return NewCoin(NewNFTID(classID, number), sdk.OneInt())
   155  }
   156  
   157  func NewCoin(id string, amount sdk.Int) Coin {
   158  	coin := Coin{
   159  		TokenId: id,
   160  		Amount:  amount,
   161  	}
   162  
   163  	if err := coin.ValidateBasic(); err != nil {
   164  		panic(err)
   165  	}
   166  
   167  	return coin
   168  }
   169  
   170  func (c Coin) String() string {
   171  	return fmt.Sprintf("%s:%s", c.TokenId, c.Amount)
   172  }
   173  
   174  func (c Coin) ValidateBasic() error {
   175  	if err := ValidateTokenID(c.TokenId); err != nil {
   176  		return err
   177  	}
   178  
   179  	if c.isNil() || !c.isPositive() {
   180  		return fmt.Errorf("invalid amount: %v", c.Amount)
   181  	}
   182  
   183  	if err := ValidateNFTID(c.TokenId); err == nil {
   184  		if !c.Amount.Equal(sdk.OneInt()) {
   185  			return fmt.Errorf("duplicate non fungible tokens")
   186  		}
   187  	}
   188  
   189  	return nil
   190  }
   191  
   192  func (c Coin) isPositive() bool {
   193  	return c.Amount.IsPositive()
   194  }
   195  
   196  func (c Coin) isNil() bool {
   197  	return c.Amount.IsNil()
   198  }
   199  
   200  var reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s%s):([[:digit:]]+)$`, patternClassID, patternAll))
   201  
   202  func ParseCoin(coinStr string) (*Coin, error) {
   203  	coinStr = strings.TrimSpace(coinStr)
   204  
   205  	matches := reDecCoin.FindStringSubmatch(coinStr)
   206  	if matches == nil {
   207  		return nil, fmt.Errorf("invalid coin expression: %s", coinStr)
   208  	}
   209  
   210  	id, amountStr := matches[1], matches[2]
   211  
   212  	amount, ok := sdk.NewIntFromString(amountStr)
   213  	if !ok {
   214  		return nil, fmt.Errorf("failed to parse coin amount: %s", amountStr)
   215  	}
   216  
   217  	coin := NewCoin(id, amount)
   218  	return &coin, nil
   219  }
   220  
   221  // ----------------------------------------------------------------------------
   222  // Coins
   223  type Coins []Coin
   224  
   225  func NewCoins(coins ...Coin) Coins {
   226  	newCoins := Coins(coins)
   227  	if err := newCoins.ValidateBasic(); err != nil {
   228  		panic(fmt.Errorf("invalid coin %s: %w", newCoins, err))
   229  	}
   230  
   231  	return newCoins
   232  }
   233  
   234  func (coins Coins) String() string {
   235  	if len(coins) == 0 {
   236  		return ""
   237  	} else if len(coins) == 1 {
   238  		return coins[0].String()
   239  	}
   240  
   241  	var out strings.Builder
   242  	for _, coin := range coins[:len(coins)-1] {
   243  		out.WriteString(coin.String())
   244  		out.WriteByte(',')
   245  	}
   246  	out.WriteString(coins[len(coins)-1].String())
   247  	return out.String()
   248  }
   249  
   250  func (coins Coins) ValidateBasic() error {
   251  	if len(coins) == 0 {
   252  		return fmt.Errorf("empty coins")
   253  	}
   254  
   255  	seenIDs := map[string]bool{}
   256  	for _, coin := range coins {
   257  		if seenIDs[coin.TokenId] {
   258  			return fmt.Errorf("duplicate id %s", coin.TokenId)
   259  		}
   260  		seenIDs[coin.TokenId] = true
   261  
   262  		if err := coin.ValidateBasic(); err != nil {
   263  			return fmt.Errorf("invalid coin %s: %w", coin.TokenId, err)
   264  		}
   265  	}
   266  
   267  	return nil
   268  }
   269  
   270  func ParseCoins(coinsStr string) (Coins, error) {
   271  	coinsStr = strings.TrimSpace(coinsStr)
   272  	if len(coinsStr) == 0 {
   273  		return nil, fmt.Errorf("invalid string for coins")
   274  	}
   275  
   276  	coinStrs := strings.Split(coinsStr, ",")
   277  	coins := make(Coins, len(coinStrs))
   278  	for i, coinStr := range coinStrs {
   279  		coin, err := ParseCoin(coinStr)
   280  		if err != nil {
   281  			return nil, err
   282  		}
   283  
   284  		coins[i] = *coin
   285  	}
   286  
   287  	return NewCoins(coins...), nil
   288  }
   289  
   290  type Token interface {
   291  	proto.Message
   292  }
   293  
   294  func TokenFromAny(any *codectypes.Any) Token {
   295  	class := any.GetCachedValue().(Token)
   296  	return class
   297  }
   298  
   299  func TokenUnpackInterfaces(any *codectypes.Any, unpacker codectypes.AnyUnpacker) error {
   300  	var token Token
   301  	return unpacker.UnpackAny(any, &token)
   302  }