Skip to content

Commit

Permalink
x/tokenfactory: add type files (CosmWasm#1361)
Browse files Browse the repository at this point in the history
* add proto files

* Update proto/osmosis/tokenfactory/v1beta1/authorityMetadata.proto

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>

* update comments on proto

* add other types files

* Apply suggestions from code review

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>

* update errors to not start from 1

* add more comments

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
  • Loading branch information
3 people authored Apr 29, 2022
1 parent bf5d52e commit 5f2453c
Show file tree
Hide file tree
Showing 9 changed files with 635 additions and 0 deletions.
35 changes: 35 additions & 0 deletions types/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package types

import (
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"

// this line is used by starport scaffolding # 1
"github.com/cosmos/cosmos-sdk/types/msgservice"
)

func RegisterCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgCreateDenom{}, "osmosis/tokenfactory/create-denom", nil)
cdc.RegisterConcrete(&MsgMint{}, "osmosis/tokenfactory/mint", nil)
cdc.RegisterConcrete(&MsgBurn{}, "osmosis/tokenfactory/burn", nil)
// cdc.RegisterConcrete(&MsgForceTransfer{}, "osmosis/tokenfactory/force-transfer", nil)
cdc.RegisterConcrete(&MsgChangeAdmin{}, "osmosis/tokenfactory/change-admin", nil)
}

func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
registry.RegisterImplementations(
(*sdk.Msg)(nil),
&MsgCreateDenom{},
&MsgMint{},
&MsgBurn{},
// &MsgForceTransfer{},
&MsgChangeAdmin{},
)
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}

var (
amino = codec.NewLegacyAmino()
ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry())
)
55 changes: 55 additions & 0 deletions types/denoms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package types

import (
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

const (
ModuleDenomPrefix = "factory"
)

// GetTokenDenom constructs a denom string for tokens created by tokenfactory
// based on an input creator address and a nonce
// The denom constructed is factory/{creator}/{nonce}
func GetTokenDenom(creator, nonce string) (string, error) {
if strings.Contains(creator, "/") {
return "", ErrInvalidCreator
}
denom := strings.Join([]string{ModuleDenomPrefix, creator, nonce}, "/")
return denom, sdk.ValidateDenom(denom)
}

// DeconstructDenom takes a token denom string and verifies that it is a valid
// denom of the tokenfactory module, and is of the form `factory/{creator}/{nonce}`
// If valid, it returns the creator address and nonce
func DeconstructDenom(denom string) (creator string, nonce string, err error) {
err = sdk.ValidateDenom(denom)
if err != nil {
return "", "", err
}

strParts := strings.Split(denom, "/")
if len(strParts) < 3 {
return "", "", sdkerrors.Wrapf(ErrInvalidDenom, "not enough parts of denom %s", denom)
}

if strParts[0] != ModuleDenomPrefix {
return "", "", sdkerrors.Wrapf(ErrInvalidDenom, "denom prefix is incorrect. Is: %s. Should be: %s", strParts[0], ModuleDenomPrefix)
}

creator = strParts[1]
_, err = sdk.AccAddressFromBech32(creator)
if err != nil {
return "", "", sdkerrors.Wrapf(ErrInvalidDenom, "Invalid creator address (%s)", err)
}

// Handle the case where a denom has a slash in its nonce. For example,
// when we did the split, we'd turn factory/sunnyaddr/atomderivative/sikka into ["factory", "sunnyaddr", "atomderivative", "sikka"]
// So we have to join [2:] with a "/" as the delimiter to get back the correct nonce which should be "atomderivative/sikka"
nonce = strings.Join(strParts[2:], "/")

return creator, nonce, nil
}
64 changes: 64 additions & 0 deletions types/denoms_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package types_test

import (
"testing"

"github.com/stretchr/testify/require"

appparams "github.com/osmosis-labs/osmosis/v7/app/params"
"github.com/osmosis-labs/osmosis/v7/x/tokenfactory/types"
)

func TestDecomposeDenoms(t *testing.T) {
appparams.SetAddressPrefixes()
for _, tc := range []struct {
desc string
denom string
valid bool
}{
{
desc: "empty is invalid",
denom: "",
valid: false,
},
{
desc: "normal",
denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
valid: true,
},
{
desc: "multiple slashes in nonce",
denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin/1",
valid: true,
},
{
desc: "no nonce",
denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/",
valid: true,
},
{
desc: "incorrect prefix",
denom: "ibc/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
valid: false,
},
{
desc: "nonce of only slashes",
denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/////",
valid: true,
},
{
desc: "too long name",
denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/adsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsf",
valid: false,
},
} {
t.Run(tc.desc, func(t *testing.T) {
_, _, err := types.DeconstructDenom(tc.denom)
if tc.valid {
require.NoError(t, err)
} else {
require.Error(t, err)
}
})
}
}
17 changes: 17 additions & 0 deletions types/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package types

// DONTCOVER

import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// x/tokenfactory module sentinel errors
var (
ErrDenomExists = sdkerrors.Register(ModuleName, 2, "denom already exists")
ErrUnauthorized = sdkerrors.Register(ModuleName, 3, "unauthorized account")
ErrInvalidDenom = sdkerrors.Register(ModuleName, 4, "invalid denom")
ErrInvalidCreator = sdkerrors.Register(ModuleName, 5, "invalid creator")
ErrInvalidAuthorityMetadata = sdkerrors.Register(ModuleName, 6, "invalid authority metadata")
ErrInvalidGenesis = sdkerrors.Register(ModuleName, 7, "invalid genesis")
)
15 changes: 15 additions & 0 deletions types/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package types

// event types
const (
AttributeAmount = "amount"
AttributeCreator = "creator"
AttributeNonce = "nonce"
AttributeNewTokenDenom = "new_token_denom"
AttributeMintToAddress = "mint_to_address"
AttributeBurnFromAddress = "burn_from_address"
AttributeTransferFromAddress = "transfer_from_address"
AttributeTransferToAddress = "transfer_to_address"
AttributeDenom = "denom"
AttributeNewAdmin = "new_admin"
)
45 changes: 45 additions & 0 deletions types/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package types

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// this line is used by starport scaffolding # genesis/types/import

// DefaultIndex is the default capability global index
const DefaultIndex uint64 = 1

// DefaultGenesis returns the default Capability genesis state
func DefaultGenesis() *GenesisState {
return &GenesisState{
FactoryDenoms: []GenesisDenom{},
}
}

// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
seenDenoms := map[string]bool{}

for _, denom := range gs.GetFactoryDenoms() {
if seenDenoms[denom.GetDenom()] {
return sdkerrors.Wrapf(ErrInvalidGenesis, "duplicate denom: %s", denom.GetDenom())
}
seenDenoms[denom.GetDenom()] = true

_, _, err := DeconstructDenom(denom.GetDenom())
if err != nil {
return err
}

if denom.AuthorityMetadata.Admin != "" {
_, err = sdk.AccAddressFromBech32(denom.AuthorityMetadata.Admin)
if err != nil {
return sdkerrors.Wrapf(ErrInvalidAuthorityMetadata, "Invalid admin address (%s)", err)
}
}
}

return nil
}
139 changes: 139 additions & 0 deletions types/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package types_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/osmosis-labs/osmosis/v7/x/tokenfactory/types"
)

func TestGenesisState_Validate(t *testing.T) {
for _, tc := range []struct {
desc string
genState *types.GenesisState
valid bool
}{
{
desc: "default is valid",
genState: types.DefaultGenesis(),
valid: true,
},
{
desc: "valid genesis state",
genState: &types.GenesisState{
FactoryDenoms: []types.GenesisDenom{
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44",
},
},
},
},
valid: true,
},
{
desc: "different admin from creator",
genState: &types.GenesisState{
FactoryDenoms: []types.GenesisDenom{
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "osmo1ft6e5esdtdegnvcr3djd3ftk4kwpcr6jrx5fj9",
},
},
},
},
valid: true,
},
{
desc: "empty admin",
genState: &types.GenesisState{
FactoryDenoms: []types.GenesisDenom{
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "",
},
},
},
},
valid: true,
},
{
desc: "no admin",
genState: &types.GenesisState{
FactoryDenoms: []types.GenesisDenom{
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
},
},
},
valid: true,
},
{
desc: "invalid admin",
genState: &types.GenesisState{
FactoryDenoms: []types.GenesisDenom{
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "moose",
},
},
},
},
valid: false,
},
{
desc: "multiple denoms",
genState: &types.GenesisState{
FactoryDenoms: []types.GenesisDenom{
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "",
},
},
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/litecoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "",
},
},
},
},
valid: true,
},
{
desc: "duplicate denoms",
genState: &types.GenesisState{
FactoryDenoms: []types.GenesisDenom{
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "",
},
},
{
Denom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/bitcoin",
AuthorityMetadata: types.DenomAuthorityMetadata{
Admin: "",
},
},
},
},
valid: false,
},
} {
t.Run(tc.desc, func(t *testing.T) {
err := tc.genState.Validate()
if tc.valid {
require.NoError(t, err)
} else {
require.Error(t, err)
}
})
}
}
Loading

0 comments on commit 5f2453c

Please sign in to comment.