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 }