From 31421e132888fda79a6004e6146a45b13611d3a7 Mon Sep 17 00:00:00 2001 From: Mauro Lacy Date: Thu, 31 Oct 2024 11:49:23 +0400 Subject: [PATCH] Mint rewards initial impl --- x/babylon/keeper/handler_plugin.go | 44 +++++++++++++++++++---- x/babylon/keeper/mint_rewards.go | 56 +++++++++++++++++++++++++++++ x/babylon/types/events.go | 1 + x/babylon/types/expected_keepers.go | 3 ++ 4 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 x/babylon/keeper/mint_rewards.go diff --git a/x/babylon/keeper/handler_plugin.go b/x/babylon/keeper/handler_plugin.go index 5a6534d..2a68cf7 100644 --- a/x/babylon/keeper/handler_plugin.go +++ b/x/babylon/keeper/handler_plugin.go @@ -1,8 +1,8 @@ package keeper import ( + sdkmath "cosmossdk.io/math" "encoding/json" - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types" @@ -22,10 +22,13 @@ type AuthSource interface { } // abstract keeper -type msKeeper interface{} +type babylonKeeper interface { + GetParams(ctx sdk.Context) types.Params + MintBlockRewards(ctx sdk.Context, recipient sdk.AccAddress, amount sdk.Coin) (sdkmath.Int, error) +} type CustomMsgHandler struct { - k msKeeper + k babylonKeeper auth AuthSource } @@ -36,7 +39,7 @@ func NewDefaultCustomMsgHandler(k *Keeper) *CustomMsgHandler { // NewCustomMsgHandler constructor to set up CustomMsgHandler with an individual auth source. // This is an extension point for non default contract authorization logic. -func NewCustomMsgHandler(k msKeeper, auth AuthSource) *CustomMsgHandler { +func NewCustomMsgHandler(k babylonKeeper, auth AuthSource) *CustomMsgHandler { return &CustomMsgHandler{k: k, auth: auth} } @@ -67,8 +70,37 @@ func (h CustomMsgHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddre return h.handleMintRewardsMsg(ctx, contractAddr, customMsg.MintRewards) } -func (h CustomMsgHandler) handleMintRewardsMsg(ctx sdk.Context, actor sdk.AccAddress, mintRewardsMsg *contract.MintRewardsMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) { - return []sdk.Event{}, nil, nil, nil +func (h CustomMsgHandler) handleMintRewardsMsg(ctx sdk.Context, actor sdk.AccAddress, mintMsg *contract.MintRewardsMsg) ([]sdk.Event, [][]byte, [][]*codectypes.Any, error) { + coin, err := wasmkeeper.ConvertWasmCoinToSdkCoin(mintMsg.Amount) + if err != nil { + return nil, nil, nil, err + } + params := h.k.GetParams(ctx) + // Validate actor + if actor.String() != params.BtcFinalityContractAddress { + return nil, nil, nil, sdkerrors.ErrUnauthorized.Wrapf("minter must be the finality contract") + } + + // Define recipient + recipient, err := sdk.AccAddressFromBech32(mintMsg.Recipient) + if err != nil { + return nil, nil, nil, err + } + if err != nil { + return nil, nil, nil, err + } + + rewards, err := h.k.MintBlockRewards(ctx, recipient, coin) + if err != nil { + return nil, nil, nil, err + } + + return []sdk.Event{sdk.NewEvent( + types.EventTypeMintRewards, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(sdk.AttributeKeySender, actor.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, rewards.String()), + )}, nil, nil, nil } // AuthSourceFn is helper for simple AuthSource types diff --git a/x/babylon/keeper/mint_rewards.go b/x/babylon/keeper/mint_rewards.go new file mode 100644 index 0000000..0d4b79e --- /dev/null +++ b/x/babylon/keeper/mint_rewards.go @@ -0,0 +1,56 @@ +package keeper + +import ( + sdkmath "cosmossdk.io/math" + "github.com/babylonlabs-io/babylon-sdk/x/babylon/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" +) + +// MintBlockRewards mints new "virtual" bonding tokens and sends them to the staking contract for distribution. +// The amount minted is removed from the SupplyOffset (so that it will become negative), when supported. +// Authorization of the actor/recipient should be handled before entering this method. +// The amount is computed based on the finality inflation rate and the total staking token supply, in the bonded denom +func (k Keeper) MintBlockRewards(pCtx sdk.Context, recipient sdk.AccAddress, amt sdk.Coin) (sdkmath.Int, error) { + if amt.Amount.IsNil() || amt.Amount.IsZero() || amt.Amount.IsNegative() { + return sdkmath.ZeroInt(), errors.ErrInvalidRequest.Wrap("amount") + } + + // Ensure staking constraints + bondDenom, err := k.Staking.BondDenom(pCtx) + if err != nil { + return sdkmath.ZeroInt(), err + } + if amt.Denom != bondDenom { + return sdkmath.ZeroInt(), errors.ErrInvalidRequest.Wrapf("invalid coin denomination: got %s, expected %s", amt.Denom, bondDenom) + } + // FIXME? Remove this constraint for flexibility + params := k.GetParams(pCtx) + if recipient.String() != params.BtcStakingContractAddress { + return sdkmath.ZeroInt(), errors.ErrUnauthorized.Wrapf("invalid recipient: got %s, expected staking contract (%s)", + recipient, params.BtcStakingContractAddress) + } + + // TODO?: Ensure Babylon constraints + + cacheCtx, done := pCtx.CacheContext() // work in a cached store as Osmosis (safety net?) + + // Mint rewards tokens + coins := sdk.NewCoins(amt) + err = k.bank.MintCoins(cacheCtx, types.ModuleName, coins) + if err != nil { + return sdkmath.ZeroInt(), err + } + + // FIXME: Confirm we want this supply offset enabled for rewards, i.e. + // as virtual coins that do not count to the total supply + //k.bank.AddSupplyOffset(cacheCtx, bondDenom, amt.Amount.Neg()) + + err = k.bank.SendCoinsFromModuleToAccount(cacheCtx, types.ModuleName, recipient, coins) + if err != nil { + return sdkmath.ZeroInt(), err + } + + done() + return amt.Amount, err +} diff --git a/x/babylon/types/events.go b/x/babylon/types/events.go index 3343914..b54895c 100644 --- a/x/babylon/types/events.go +++ b/x/babylon/types/events.go @@ -12,6 +12,7 @@ const ( EventTypeMaxCapLimitUpdated = "max_cap_limit_updated" EventTypeUnbond = "instant_unbond" EventTypeDelegate = "instant_delegate" + EventTypeMintRewards = "mint_rewards" ) const ( diff --git a/x/babylon/types/expected_keepers.go b/x/babylon/types/expected_keepers.go index 56fe6e7..d7b4e53 100644 --- a/x/babylon/types/expected_keepers.go +++ b/x/babylon/types/expected_keepers.go @@ -2,6 +2,7 @@ package types import ( context "context" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -18,6 +19,8 @@ type BankKeeper interface { // StakingKeeper expected staking keeper. type StakingKeeper interface { + BondDenom(ctx context.Context) (string, error) + StakingTokenSupply(ctx context.Context) (sdkmath.Int, error) } // AccountKeeper interface contains functions for getting accounts and the module address