github.com/InjectiveLabs/sdk-go@v1.53.0/chain/tokenfactory/types/denoms.go (about)

     1  package types
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"cosmossdk.io/errors"
     9  	sdk "github.com/cosmos/cosmos-sdk/types"
    10  	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
    11  )
    12  
    13  const (
    14  	ModuleDenomPrefix = "factory"
    15  	// See the TokenFactory readme for a derivation of these.
    16  	// TL;DR, MaxSubdenomLength + MaxHrpLength = 60 comes from SDK max denom length = 128
    17  	// and the structure of tokenfactory denoms.
    18  	MaxSubdenomLength = 44
    19  	MaxHrpLength      = 16
    20  	// MaxCreatorLength = 59 + MaxHrpLength
    21  	MaxCreatorLength  = 59 + MaxHrpLength
    22  	MinSubdenomLength = 1
    23  )
    24  
    25  // GetTokenDenom constructs a denom string for tokens created by tokenfactory
    26  // based on an input creator address and a subdenom
    27  // The denom constructed is factory/{creator}/{subdenom}
    28  func GetTokenDenom(creator, subdenom string) (string, error) {
    29  	if len(subdenom) > MaxSubdenomLength {
    30  		return "", ErrSubdenomTooLong
    31  	}
    32  	if len(subdenom) < MinSubdenomLength {
    33  		return "", ErrSubdenomTooShort
    34  	}
    35  
    36  	strParts := strings.Split(subdenom, "/")
    37  
    38  	for idx := range strParts {
    39  		if len(strParts[idx]) < MinSubdenomLength {
    40  			return "", ErrSubdenomNestedTooShort
    41  		}
    42  	}
    43  
    44  	if len(creator) > MaxCreatorLength {
    45  		return "", ErrCreatorTooLong
    46  	}
    47  	if strings.Contains(creator, "/") || len(creator) < 1 {
    48  		return "", ErrInvalidCreator
    49  	}
    50  
    51  	// ensure creator address is in lowercase Bech32 form
    52  	creatorAddr, err := sdk.AccAddressFromBech32(creator)
    53  	if err != nil {
    54  		return "", err
    55  	}
    56  
    57  	denom := strings.Join([]string{ModuleDenomPrefix, creatorAddr.String(), subdenom}, "/")
    58  	return denom, sdk.ValidateDenom(denom)
    59  }
    60  
    61  // DeconstructDenom takes a token denom string and verifies that it is a valid
    62  // denom of the tokenfactory module, and is of the form `factory/{creator}/{subdenom}`
    63  // If valid, it returns the creator address and subdenom
    64  func DeconstructDenom(denom string) (creator, subdenom string, err error) {
    65  	err = sdk.ValidateDenom(denom)
    66  	if err != nil {
    67  		return "", "", err
    68  	}
    69  
    70  	strParts := strings.Split(denom, "/")
    71  	if len(strParts) < 3 {
    72  		return "", "", errors.Wrapf(ErrInvalidDenom, "not enough parts of denom %s", denom)
    73  	}
    74  
    75  	if strParts[0] != ModuleDenomPrefix {
    76  		return "", "", errors.Wrapf(ErrInvalidDenom, "denom prefix is incorrect. Is: %s.  Should be: %s", strParts[0], ModuleDenomPrefix)
    77  	}
    78  
    79  	creator = strParts[1]
    80  	creatorAddr, err := sdk.AccAddressFromBech32(creator)
    81  	if err != nil {
    82  		return "", "", errors.Wrapf(ErrInvalidDenom, "Invalid creator address (%s)", err)
    83  	}
    84  
    85  	if len(strParts[2]) < MinSubdenomLength {
    86  		return "", "", errors.Wrapf(ErrSubdenomTooShort, "subdenom too short. Has length of: %d.  Should have length at least of: %d", len(strParts[2]), MinSubdenomLength)
    87  	}
    88  
    89  	if len(strParts) > 3 {
    90  		for i := 2; i <= len(strParts)-1; i++ {
    91  			if len(strParts[i]) < MinSubdenomLength {
    92  				return "", "", errors.Wrapf(ErrSubdenomNestedTooShort, "nested subdenom too short. Has length of: %d.  Should have length at least of: %d", len(strParts[i]), MinSubdenomLength)
    93  			}
    94  		}
    95  	}
    96  
    97  	// Handle the case where a denom has a slash in its subdenom. For example,
    98  	// when we did the split, we'd turn factory/accaddr/atomderivative/sikka into ["factory", "accaddr", "atomderivative", "sikka"]
    99  	// So we have to join [2:] with a "/" as the delimiter to get back the correct subdenom which should be "atomderivative/sikka"
   100  	subdenom = strings.Join(strParts[2:], "/")
   101  
   102  	return creatorAddr.String(), subdenom, nil
   103  }
   104  
   105  // NewTokenFactoryDenomMintCoinsRestriction creates and returns a MintingRestrictionFn that only allows minting of
   106  // valid tokenfactory denoms
   107  func NewTokenFactoryDenomMintCoinsRestriction() banktypes.MintingRestrictionFn {
   108  	return func(ctx context.Context, coinsToMint sdk.Coins) error {
   109  		for _, coin := range coinsToMint {
   110  			_, _, err := DeconstructDenom(coin.Denom)
   111  			if err != nil {
   112  				return fmt.Errorf("does not have permission to mint %s", coin.Denom)
   113  			}
   114  		}
   115  		return nil
   116  	}
   117  }