Skip to content

Commit

Permalink
feat: Ability to SudoMint tokens (optional)
Browse files Browse the repository at this point in the history
  • Loading branch information
Reecepbcups committed Feb 10, 2024
1 parent f164e8b commit cea17e9
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 29 deletions.
2 changes: 2 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ var (
tokenfactorytypes.EnableBurnFrom,
tokenfactorytypes.EnableForceTransfer,
tokenfactorytypes.EnableSetMetadata,
tokenfactorytypes.EnableSudoMint,
}
)

Expand Down Expand Up @@ -550,6 +551,7 @@ func NewApp(
app.BankKeeper,
app.DistrKeeper,
tokenFactoryCapabilities,
tokenfactorykeeper.DefaultIsSudoAdminFunc,
govModAddress,
)
wasmOpts = append(wasmOpts, bindings.RegisterCustomPlugins(app.BankKeeper, &app.TokenFactoryKeeper)...)
Expand Down
12 changes: 7 additions & 5 deletions x/tokenfactory/keeper/bankactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

func (k Keeper) mintTo(ctx sdk.Context, amount sdk.Coin, mintTo string) error {
func (k Keeper) mintTo(ctx sdk.Context, amount sdk.Coin, mintTo string, isSudo bool) error {
// verify that denom is an x/tokenfactory denom
_, _, err := types.DeconstructDenom(amount.Denom)
if err != nil {
return err
if !isSudo {
_, _, err := types.DeconstructDenom(amount.Denom)
if err != nil {
return err
}
}

err = k.bankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount))
err := k.bankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount))
if err != nil {
return err
}
Expand Down
16 changes: 11 additions & 5 deletions x/tokenfactory/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import (
)

type (
// IsAdmin is a function signature that checks if an address is an admin.
IsSudoAdmin func(ctx context.Context, addr string) bool

Keeper struct {
cdc codec.BinaryCodec
storeKey store.StoreKey
Expand All @@ -28,6 +31,8 @@ type (
// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string

IsSudoAdminFunc IsSudoAdmin
}
)

Expand All @@ -39,6 +44,8 @@ func NewKeeper(
bankKeeper types.BankKeeper,
communityPoolKeeper types.CommunityPoolKeeper,
enabledCapabilities []string,
// use DefaultIsSudoAdminFunc if you don't have a custom one
isSudoAdminFunc IsSudoAdmin,
authority string,
) Keeper {
return Keeper{
Expand All @@ -52,14 +59,13 @@ func NewKeeper(
authority: authority,

enabledCapabilities: enabledCapabilities,
}
}

func DefaultIsAdminFunc(ctx context.Context, addr string) bool {
return false
IsSudoAdminFunc: isSudoAdminFunc,
}
}

func DefaultExtraSudoAllowedCheckFunc(ctx context.Context) bool {
// DefaultIsSudoAdminFunc returns false for all addresses.
func DefaultIsSudoAdminFunc(ctx context.Context, addr string) bool {
return false
}

Expand Down
4 changes: 4 additions & 0 deletions x/tokenfactory/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ func (suite *KeeperTestSuite) CreateDefaultDenom() {
res, _ := suite.msgServer.CreateDenom(suite.Ctx, types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
suite.defaultDenom = res.GetNewTokenDenom()
}

func (suite *KeeperTestSuite) OverrideMsgServer(newKeeper keeper.Keeper) {
suite.msgServer = keeper.NewMsgServerImpl(newKeeper)
}
30 changes: 18 additions & 12 deletions x/tokenfactory/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,32 @@ func (server msgServer) Mint(goCtx context.Context, msg *types.MsgMint) (*types.
var err error
ctx := sdk.UnwrapSDKContext(goCtx)

// Standard User Verification
_, denomExists := server.bankKeeper.GetDenomMetaData(ctx, msg.Amount.Denom)
if !denomExists {
return nil, types.ErrDenomDoesNotExist.Wrapf("denom: %s", msg.Amount.Denom)
}
sudoEnabled := types.IsCapabilityEnabled(server.Keeper.enabledCapabilities, types.EnableSudoMint)
senderIsSudoAble := server.IsSudoAdminFunc(goCtx, msg.Sender)
isSudo := sudoEnabled && senderIsSudoAble

authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Amount.GetDenom())
if err != nil {
return nil, err
}
if !isSudo {
// Standard user verification if they are not a Sudo admin
_, denomExists := server.bankKeeper.GetDenomMetaData(ctx, msg.Amount.Denom)
if !denomExists {
return nil, types.ErrDenomDoesNotExist.Wrapf("denom: %s", msg.Amount.Denom)
}

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

if msg.Sender != authorityMetadata.GetAdmin() {
return nil, types.ErrUnauthorized
}
}

if msg.MintToAddress == "" {
msg.MintToAddress = msg.Sender
}

err = server.Keeper.mintTo(ctx, msg.Amount, msg.MintToAddress)
err = server.Keeper.mintTo(ctx, msg.Amount, msg.MintToAddress, isSudo)
if err != nil {
return nil, err
}
Expand Down
33 changes: 26 additions & 7 deletions x/tokenfactory/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper_test

import (
"context"
"fmt"

"github.com/reecepbcups/tokenfactory/x/tokenfactory/types"
Expand All @@ -21,8 +22,8 @@ func (suite *KeeperTestSuite) TestMintDenomMsg() {
amount int64
mintDenom string
admin string
sudoer string
expectedMessageEvents int // the valid case should emit >= 1
inflation sdkmath.LegacyDec
}{
{
desc: "denom does not exist",
Expand All @@ -37,19 +38,37 @@ func (suite *KeeperTestSuite) TestMintDenomMsg() {
admin: suite.TestAccs[0].String(),
expectedMessageEvents: 1,
},
// Sudo Mints
{
desc: "successful sudo mint executed by an allowed sudoer",
amount: 10,
mintDenom: "unique",
admin: suite.TestAccs[0].String(),
sudoer: suite.TestAccs[0].String(), // this user can sudo mint
expectedMessageEvents: 1,
},
{
desc: "invalid sudo mint from a non admin",
amount: 10,
mintDenom: "unique",
admin: suite.TestAccs[0].String(),
sudoer: "nope",
},
} {
suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
suite.Require().Equal(0, len(ctx.EventManager().Events()))

// set minter values
minter, err := suite.App.MintKeeper.Minter.Get(ctx)
suite.Require().NoError(err)
minter.Inflation = tc.inflation
suite.Require().NoError(suite.App.MintKeeper.Minter.Set(ctx, minter))
// Override the default IsSudoAdminFunc for testing
suite.App.TokenFactoryKeeper.IsSudoAdminFunc = func(ctx context.Context, addr string) bool {
return tc.sudoer == addr
}

suite.OverrideMsgServer(suite.App.TokenFactoryKeeper)

// Test mint message
suite.msgServer.Mint(ctx, types.NewMsgMint(tc.admin, sdk.NewInt64Coin(tc.mintDenom, 10))) //nolint:errcheck
suite.msgServer.Mint(ctx, types.NewMsgMint(tc.admin, sdk.NewInt64Coin(tc.mintDenom, tc.amount))) //nolint:errcheck

// Ensure current number and type of event is emitted
suite.AssertEventEmitted(ctx, types.TypeMsgMint, tc.expectedMessageEvents)
})
Expand Down
3 changes: 3 additions & 0 deletions x/tokenfactory/types/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const (
EnableSetMetadata = "enable_metadata"
EnableForceTransfer = "enable_force_transfer"
EnableBurnFrom = "enable_burn_from"
// Allows addresses of your choosing to mint tokens based on specific conditions.
// via the IsSudoAdminFunc
EnableSudoMint = "enable_admin_sudo_mint"
)

func IsCapabilityEnabled(enabledCapabilities []string, capability string) bool {
Expand Down

0 comments on commit cea17e9

Please sign in to comment.