Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ var (
DefaultNodeHome = ".tokend"
BinaryName = "tokend"

tokenFactoryCapabilities = []string{
TokenFactoryAllCapabilities = []string{
tokenfactorytypes.EnableBurnOwn,
tokenfactorytypes.EnableBurnOwnUnregistered,
tokenfactorytypes.EnableBurnFrom,
tokenfactorytypes.EnableForceTransfer,
tokenfactorytypes.EnableSetMetadata,
Expand Down Expand Up @@ -551,7 +552,7 @@ func NewApp(
app.AccountKeeper,
app.BankKeeper,
app.DistrKeeper,
tokenFactoryCapabilities,
TokenFactoryAllCapabilities,
govModAddress,
)
wasmOpts = append(wasmOpts, bindings.RegisterCustomPlugins(app.BankKeeper, &app.TokenFactoryKeeper)...)
Expand Down
26 changes: 14 additions & 12 deletions x/tokenfactory/bindings/validate_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ func TestChangeAdmin(t *testing.T) {

tokenCreator := RandomAccountAddress()

specs := map[string]struct {
type Spec struct {
actor sdk.AccAddress
changeAdmin *bindings.ChangeAdmin
expErrMsg func(Spec) string
}

expErrMsg string
}{
specs := map[string]Spec{
"valid": {
changeAdmin: &bindings.ChangeAdmin{
Denom: fmt.Sprintf("factory/%s/%s", tokenCreator.String(), validDenom),
Expand All @@ -87,47 +88,47 @@ func TestChangeAdmin(t *testing.T) {
NewAdminAddress: RandomBech32AccountAddress(),
},
actor: tokenCreator,
expErrMsg: "denom prefix is incorrect. Is: facory. Should be: factory: invalid denom",
expErrMsg: func(_ Spec) string { return "denom prefix is incorrect. Is: facory. Should be: factory: invalid denom" },
},
"invalid address in denom": {
changeAdmin: &bindings.ChangeAdmin{
Denom: fmt.Sprintf("factory/%s/%s", RandomBech32AccountAddress(), validDenom),
NewAdminAddress: RandomBech32AccountAddress(),
},
actor: tokenCreator,
expErrMsg: "failed changing admin from message: unauthorized account",
expErrMsg: func(s Spec) string { return fmt.Sprintf("failed changing admin from message: denom \"%s\": denom is not registered with tokenfactory", s.changeAdmin.Denom) },
},
"other denom name in 3 part name": {
changeAdmin: &bindings.ChangeAdmin{
Denom: fmt.Sprintf("factory/%s/%s", tokenCreator.String(), "invalid denom"),
NewAdminAddress: RandomBech32AccountAddress(),
},
actor: tokenCreator,
expErrMsg: fmt.Sprintf("invalid denom: factory/%s/invalid denom", tokenCreator.String()),
expErrMsg: func(s Spec) string { return fmt.Sprintf("invalid denom: %s", s.changeAdmin.Denom) },
},
"empty denom": {
changeAdmin: &bindings.ChangeAdmin{
Denom: "",
NewAdminAddress: RandomBech32AccountAddress(),
},
actor: tokenCreator,
expErrMsg: "invalid denom: ",
expErrMsg: func(_ Spec) string { return "invalid denom: " },
},
"empty address": {
changeAdmin: &bindings.ChangeAdmin{
Denom: fmt.Sprintf("factory/%s/%s", tokenCreator.String(), validDenom),
NewAdminAddress: "",
},
actor: tokenCreator,
expErrMsg: "address from bech32: empty address string is not allowed",
expErrMsg: func(_ Spec) string { return "address from bech32: empty address string is not allowed" },
},
"creator is a different address": {
changeAdmin: &bindings.ChangeAdmin{
Denom: fmt.Sprintf("factory/%s/%s", tokenCreator.String(), validDenom),
NewAdminAddress: RandomBech32AccountAddress(),
},
actor: RandomAccountAddress(),
expErrMsg: "failed changing admin from message: unauthorized account",
expErrMsg: func(_ Spec) string { return "failed changing admin from message: unauthorized account" },
},
"change to the same address": {
changeAdmin: &bindings.ChangeAdmin{
Expand All @@ -138,7 +139,7 @@ func TestChangeAdmin(t *testing.T) {
},
"nil binding": {
actor: tokenCreator,
expErrMsg: "invalid request: changeAdmin is nil - original request: ",
expErrMsg: func(_ Spec) string { return "invalid request: changeAdmin is nil - original request: " },
},
}
for name, spec := range specs {
Expand All @@ -156,10 +157,11 @@ func TestChangeAdmin(t *testing.T) {
require.NoError(t, err)

err = wasmbinding.ChangeAdmin(&app.TokenFactoryKeeper, ctx, spec.actor, spec.changeAdmin)
if len(spec.expErrMsg) > 0 {
if spec.expErrMsg != nil {
require.Error(t, err)
actualErrMsg := err.Error()
require.Equal(t, spec.expErrMsg, actualErrMsg)
expectedMsg := spec.expErrMsg(spec)
require.Equal(t, expectedMsg, actualErrMsg)
return
}
require.NoError(t, err)
Expand Down
24 changes: 21 additions & 3 deletions x/tokenfactory/bindings/validate_queries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

wasmbinding "github.com/strangelove-ventures/tokenfactory/x/tokenfactory/bindings"
"github.com/strangelove-ventures/tokenfactory/x/tokenfactory/keeper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -74,10 +75,15 @@ func TestDenomAdmin(t *testing.T) {

// create a subdenom via the token factory
admin := sdk.AccAddress([]byte("addr1_______________"))
tfDenom, err := app.TokenFactoryKeeper.CreateDenom(ctx, admin.String(), "subdenom")
validSubDenom := "validdenom"
tfDenom, err := app.TokenFactoryKeeper.CreateDenom(ctx, admin.String(), validSubDenom)
require.NoError(t, err)
require.NotEmpty(t, tfDenom)

registeredUnboundDenom := "registered"
udc := keeper.NewUnboundDenomCreator(app.TokenFactoryKeeper)
assert.NoError(t, udc.CreateDenom(ctx, admin.String(), registeredUnboundDenom))

queryPlugin := wasmbinding.NewQueryPlugin(app.BankKeeper, &app.TokenFactoryKeeper)

testCases := []struct {
Expand All @@ -92,11 +98,23 @@ func TestDenomAdmin(t *testing.T) {
expectAdmin: admin.String(),
},
{
name: "invalid token factory denom",
name: "unregistered valid token factory denom",
denom: fmt.Sprintf("factory/%s/%s", RandomBech32AccountAddress(), validSubDenom),
expectErr: true,
expectAdmin: "",
},
{
name: "unregistered unbound denom",
denom: "uosmo",
expectErr: false,
expectErr: true,
Comment thread
pbukva marked this conversation as resolved.
expectAdmin: "",
},
{
name: "registered unbound denom",
denom: registeredUnboundDenom,
expectErr: false,
expectAdmin: admin.String(),
},
}

for _, tc := range testCases {
Expand Down
4 changes: 4 additions & 0 deletions x/tokenfactory/keeper/admins.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import (
func (k Keeper) GetAuthorityMetadata(ctx context.Context, denom string) (types.DenomAuthorityMetadata, error) {
bz := k.GetDenomPrefixStore(sdk.UnwrapSDKContext(ctx), denom).Get([]byte(types.DenomAuthorityMetadataKey))

if bz == nil {
return types.DenomAuthorityMetadata{}, types.ErrDenomIsNotRegistered.Wrapf("denom \"%s\"", denom)
}
Comment thread
pbukva marked this conversation as resolved.

metadata := types.DenomAuthorityMetadata{}
err := proto.Unmarshal(bz, &metadata)
if err != nil {
Expand Down
22 changes: 14 additions & 8 deletions x/tokenfactory/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,19 @@ func (server msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.
isBurningOwn = true
}

authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Amount.GetDenom())
isRegistered := err == nil
if !isRegistered && !server.IsCapabilityEnabled(types.EnableBurnOwnUnregistered) {
return nil, err
}

// The following code section is exclusively for case when:
// * either burning someone's else's tokens
// * or burning own tokens, but EnableBurnOwn is disabled
if !(isBurningOwn && server.IsCapabilityEnabled(types.EnableBurnOwn)) {
if !server.IsCapabilityEnabled(types.EnableBurnFrom) {
// Denom admin *can* burn its own tokens even if the EnableBurnFrom is *disabled*.
// This is sensical, as the admin burns it sown tokens and *not* tokens from another account.
if !isBurningOwn && !server.IsCapabilityEnabled(types.EnableBurnFrom) {
return nil, types.ErrCapabilityNotEnabled.Wrapf("the '%s' capability is NOT enabled", types.EnableBurnFrom)
}

Expand All @@ -109,17 +120,12 @@ func (server msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.
}
}

authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Amount.GetDenom())
if err != nil {
return nil, err
}

if msg.Sender != authorityMetadata.GetAdmin() {
if !isRegistered || msg.Sender != authorityMetadata.GetAdmin() {
return nil, types.ErrUnauthorized.Wrapf("the '%s' sender is NOT '%s' admin of the '%s' denomination", msg.Sender, authorityMetadata.GetAdmin(), msg.Amount.GetDenom())
}
}

err := server.Keeper.burnFrom(ctx, msg.Amount, msg.BurnFromAddress)
err = server.Keeper.burnFrom(ctx, msg.Amount, msg.BurnFromAddress)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading