diff --git a/app/app.go b/app/app.go index 9a8261a8..33439649 100644 --- a/app/app.go +++ b/app/app.go @@ -51,6 +51,7 @@ import ( "github.com/atomone-hub/atomone/app/keepers" "github.com/atomone-hub/atomone/app/params" "github.com/atomone-hub/atomone/app/upgrades" + v2 "github.com/atomone-hub/atomone/app/upgrades/v2" govtypes "github.com/atomone-hub/atomone/x/gov/types" ) @@ -58,7 +59,7 @@ var ( // DefaultNodeHome default home directories for the application daemon DefaultNodeHome string - Upgrades = []upgrades.Upgrade{} + Upgrades = []upgrades.Upgrade{v2.Upgrade} ) var ( diff --git a/app/upgrades/v2/constants.go b/app/upgrades/v2/constants.go new file mode 100644 index 00000000..2b184a3d --- /dev/null +++ b/app/upgrades/v2/constants.go @@ -0,0 +1,23 @@ +package v2 + +import ( + store "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/atomone-hub/atomone/app/upgrades" + photontypes "github.com/atomone-hub/atomone/x/photon/types" +) + +const ( + UpgradeName = "v2" +) + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: CreateUpgradeHandler, + StoreUpgrades: store.StoreUpgrades{ + Added: []string{ + // new module added in v2 + photontypes.ModuleName, + }, + }, +} diff --git a/app/upgrades/v2/upgrades.go b/app/upgrades/v2/upgrades.go new file mode 100644 index 00000000..95930246 --- /dev/null +++ b/app/upgrades/v2/upgrades.go @@ -0,0 +1,66 @@ +package v2 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + "github.com/atomone-hub/atomone/app/keepers" +) + +// CreateUpgradeHandler returns a upgrade handler for AtomOne v2 +// which executes the following migrations: +// - add new denom metadata for photon in the bank module store. +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + keepers *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + ctx.Logger().Info("Starting module migrations...") + // RunMigrations will detect the add of the photon module, will initiate + // its genesis and will fill the versionMap with its consensus version. + vm, err := mm.RunMigrations(ctx, configurator, vm) + if err != nil { + return vm, err + } + // Add the photon denom metadata to the bank module store + setPhotonDenomMetadata(ctx, keepers.BankKeeper) + ctx.Logger().Info("Upgrade complete") + return vm, nil + } +} + +func setPhotonDenomMetadata(ctx sdk.Context, bk bankkeeper.Keeper) { + ctx.Logger().Info("Adding photon denom metadata...") + bk.SetDenomMetaData(ctx, banktypes.Metadata{ + Base: "uphoton", + Display: "photon", + Name: "AtomOne Photon", + Symbol: "PHOTON", + Description: "The fee token of AtomOne Hub", + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: "uphoton", + Exponent: 0, + Aliases: []string{ + "microphoton", + }, + }, + { + Denom: "mphoton", + Exponent: 3, + Aliases: []string{ + "milliphoton", + }, + }, + { + Denom: "photon", + Exponent: 6, + }, + }, + }) + ctx.Logger().Info("Photon denom metadata added") +} diff --git a/x/photon/keeper/msg_server.go b/x/photon/keeper/msg_server.go index 7b90a0dd..40a32cd2 100644 --- a/x/photon/keeper/msg_server.go +++ b/x/photon/keeper/msg_server.go @@ -42,14 +42,14 @@ func (k msgServer) MintPhoton(goCtx context.Context, msg *types.MsgMintPhoton) ( uphotonSupply = k.bankKeeper.GetSupply(ctx, types.Denom).Amount.ToLegacyDec() conversionRate = k.conversionRate(ctx, bondDenomSupply, uphotonSupply) bondDenomToBurn = msg.Amount - uphotonToMint = bondDenomToBurn.Amount.ToLegacyDec().Mul(conversionRate) + uphotonToMint = bondDenomToBurn.Amount.ToLegacyDec().Mul(conversionRate).RoundInt() ) // If no photon to mint, do not burn bondDenomToBurn, returns an error if uphotonToMint.IsZero() { - return nil, types.ErrNoMintablePhotons + return nil, types.ErrZeroMintPhotons } // If photonToMint + photonSupply > photonMaxSupply, returns an error - if uphotonSupply.Add(uphotonToMint).GT(sdk.NewDec(types.MaxSupply)) { + if uphotonSupply.Add(uphotonToMint.ToLegacyDec()).GT(sdk.NewDec(types.MaxSupply)) { return nil, types.ErrNotEnoughPhotons } @@ -60,7 +60,7 @@ func (k msgServer) MintPhoton(goCtx context.Context, msg *types.MsgMintPhoton) ( // 4) move PHOTONs from this module address to msg signer address var ( coinsToBurn = sdk.NewCoins(bondDenomToBurn) - coinsToMint = sdk.NewCoins(sdk.NewCoin(types.Denom, uphotonToMint.RoundInt())) + coinsToMint = sdk.NewCoins(sdk.NewCoin(types.Denom, uphotonToMint)) ) // 1) Send atone to photon module for burn to, err := sdk.AccAddressFromBech32(msg.ToAddress) diff --git a/x/photon/keeper/msg_server_test.go b/x/photon/keeper/msg_server_test.go index 4db659fb..86415910 100644 --- a/x/photon/keeper/msg_server_test.go +++ b/x/photon/keeper/msg_server_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "math" "testing" "github.com/stretchr/testify/require" @@ -64,7 +65,7 @@ func TestMsgServerMintPhoton(t *testing.T) { Return(sdk.NewInt64Coin(appparams.BondDenom, atoneSupply)) m.BankKeeper.EXPECT().GetSupply(ctx, types.Denom).Return(sdk.NewInt64Coin(types.Denom, types.MaxSupply)) }, - expectedErr: "no more photon can be minted", + expectedErr: "no mintable photon after rounding, try higher burn", }, { name: "fail: photon_supply+minted>max", @@ -81,6 +82,21 @@ func TestMsgServerMintPhoton(t *testing.T) { }, expectedErr: "not enough photon can be minted", }, + { + name: "fail: atone_supply >> photon_supply", + params: types.Params{MintDisabled: false}, + msg: &types.MsgMintPhoton{ + ToAddress: toAddress.String(), + Amount: sdk.NewInt64Coin(appparams.BondDenom, 1), + }, + setup: func(ctx sdk.Context, m testutil.Mocks) { + m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) + m.BankKeeper.EXPECT().GetSupply(ctx, appparams.BondDenom). + Return(sdk.NewInt64Coin(appparams.BondDenom, math.MaxInt)) + m.BankKeeper.EXPECT().GetSupply(ctx, types.Denom).Return(sdk.NewInt64Coin(types.Denom, 0)) + }, + expectedErr: "no mintable photon after rounding, try higher burn", + }, { name: "ok: photon_supply=0", params: types.Params{MintDisabled: false}, diff --git a/x/photon/types/errors.go b/x/photon/types/errors.go index 2cfd7386..031b7eb1 100644 --- a/x/photon/types/errors.go +++ b/x/photon/types/errors.go @@ -6,11 +6,10 @@ import ( // x/photon module sentinel errors var ( - ErrMintDisabled = sdkerrors.Register(ModuleName, 1, "photon mint disabled") //nolint:staticcheck - ErrBurnInvalidDenom = sdkerrors.Register(ModuleName, 2, "invalid burned amount denom: expected bond denom") //nolint:staticcheck - ErrNoMintablePhotons = sdkerrors.Register(ModuleName, 3, "no more photon can be minted") //nolint:staticcheck - ErrNotEnoughPhotons = sdkerrors.Register(ModuleName, 4, "not enough photon can be minted") //nolint:staticcheck - ErrTooManyFeeCoins = sdkerrors.Register(ModuleName, 5, "too many fee coins, only accepts fees in one denom") //nolint:staticcheck - ErrInvalidFeeToken = sdkerrors.Register(ModuleName, 6, "invalid fee token") //nolint:staticcheck - + ErrMintDisabled = sdkerrors.Register(ModuleName, 1, "photon mint disabled") //nolint:staticcheck + ErrBurnInvalidDenom = sdkerrors.Register(ModuleName, 2, "invalid burned amount denom: expected bond denom") //nolint:staticcheck + ErrZeroMintPhotons = sdkerrors.Register(ModuleName, 3, "no mintable photon after rounding, try higher burn") //nolint:staticcheck + ErrNotEnoughPhotons = sdkerrors.Register(ModuleName, 4, "not enough photon can be minted") //nolint:staticcheck + ErrTooManyFeeCoins = sdkerrors.Register(ModuleName, 5, "too many fee coins, only accepts fees in one denom") //nolint:staticcheck + ErrInvalidFeeToken = sdkerrors.Register(ModuleName, 6, "invalid fee token") //nolint:staticcheck )