Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XATP module #73

Merged
merged 29 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a0e03d8
add xatp
green-blue0 Nov 21, 2022
654a3f9
fix: correct proto path
JoowonYun Dec 7, 2022
26debd3
fix: correct module name
JoowonYun Dec 7, 2022
9fd2c18
fix: mempool fee decorator test
JoowonYun Dec 7, 2022
743ae14
refactor: remove unused code
JoowonYun Dec 8, 2022
fe65d2b
feat: changed to manage XATP with map
JoowonYun Dec 12, 2022
0327ac0
feat: add to xatp query
JoowonYun Dec 12, 2022
dcb2dfa
refactor: simple conditions and calculations
JoowonYun Dec 16, 2022
724d2ae
feat: manage XATP as a proposal
JoowonYun Dec 16, 2022
da526e0
refactor: remove unused SmartQueryGasLimit
JoowonYun Dec 16, 2022
c21fe0f
refactor: ante MempoolFeeDecorator
JoowonYun Dec 16, 2022
1e43514
refactor: ante DeductFeeDecorator
JoowonYun Dec 16, 2022
a62b34c
fix: exception handling when payer is empty in xatp module
JoowonYun Dec 22, 2022
2c4a2b1
fix: fee precision
JoowonYun Dec 26, 2022
d31f727
feat: change payer to module account
JoowonYun Mar 8, 2023
6d72fdd
feat: apply tax-rate
JoowonYun Mar 8, 2023
0846eb1
feat: xatp economy
JoowonYun Mar 10, 2023
2b85c9b
feat: fund xatp pool & query pool
JoowonYun Mar 10, 2023
3fa7432
fix: typo
JoowonYun Mar 13, 2023
d380341
fix: validate simulation
JoowonYun Mar 13, 2023
946704a
refactor: merge upgrade (xatp & align gas price)
JoowonYun Mar 14, 2023
fd803ab
feat: gas refund when mempool check
JoowonYun Mar 14, 2023
79dbd98
refactor: increased precision in simulate
JoowonYun Mar 15, 2023
f839df8
fix: change XATP from dec to int
JoowonYun Mar 17, 2023
0efc6e4
fix: change the xatp param rate to be greater than 1
JoowonYun Mar 20, 2023
ba0cb66
fix: calculate reward pool
JoowonYun Mar 20, 2023
3534eec
fix: check fee when simulate
JoowonYun Mar 22, 2023
e58389a
Added integartion tests for XATP module (#60)
psy2848048 Mar 23, 2023
d4a7f7c
refactor: variable names
JoowonYun Mar 30, 2023
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
14 changes: 10 additions & 4 deletions ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
evmante "github.com/evmos/ethermint/app/ante"
evmtypes "github.com/evmos/ethermint/x/evm/types"
tmlog "github.com/tendermint/tendermint/libs/log"

xatpkeeper "github.com/xpladev/xpla/x/xatp/keeper"
)

// HandlerOptions extend the SDK's AnteHandler opts by requiring the IBC
Expand All @@ -35,6 +37,8 @@ type HandlerOptions struct {
BypassMinFeeMsgTypes []string
TxCounterStoreKey sdk.StoreKey
WasmConfig wasmTypes.WasmConfig
XATPKeeper xatpkeeper.Keeper
MinGasPrices string
}

// NewAnteHandler returns an 'AnteHandler' that will run actions before a tx is sent to a module's handler.
Expand Down Expand Up @@ -99,13 +103,15 @@ func newCosmosAnteHandler(opts HandlerOptions) sdk.AnteHandler {
wasmkeeper.NewLimitSimulationGasDecorator(opts.WasmConfig.SimulationGasLimit),
wasmkeeper.NewCountTXDecorator(opts.TxCounterStoreKey),
authante.NewRejectExtensionOptionsDecorator(),
NewMinGasPriceDecorator(opts.FeeMarketKeeper, opts.EvmKeeper, opts.BypassMinFeeMsgTypes),
NewMempoolFeeDecorator(opts.BypassMinFeeMsgTypes, opts.AccountKeeper, opts.XATPKeeper, opts.FeeMarketKeeper, opts.EvmKeeper),
authante.NewValidateBasicDecorator(),
authante.NewTxTimeoutHeightDecorator(),
authante.NewValidateMemoDecorator(opts.AccountKeeper),
authante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper),
authante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper),
authante.NewSetPubKeyDecorator(opts.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.XATPKeeper),
// SetPubKeyDecorator must be called before all signature verification decorators
authante.NewSetPubKeyDecorator(opts.AccountKeeper),
authante.NewValidateSigCountDecorator(opts.AccountKeeper),
authante.NewSigGasConsumeDecorator(opts.AccountKeeper, sigGasConsumer),
authante.NewSigVerificationDecorator(opts.AccountKeeper, opts.SignModeHandler),
Expand All @@ -117,8 +123,8 @@ func newCosmosAnteHandler(opts HandlerOptions) sdk.AnteHandler {

func newEthAnteHandler(opts HandlerOptions) sdk.AnteHandler {
return sdk.ChainAnteDecorators(
evmante.NewEthSetUpContextDecorator(opts.EvmKeeper), // outermost AnteDecorator. SetUpContext must be called first
NewMinGasPriceDecorator(opts.FeeMarketKeeper, opts.EvmKeeper, opts.BypassMinFeeMsgTypes), // Check eth effective gas price against the global MinGasPrice
evmante.NewEthSetUpContextDecorator(opts.EvmKeeper), // outermost AnteDecorator. SetUpContext must be called first
NewMempoolFeeDecorator(opts.BypassMinFeeMsgTypes, opts.AccountKeeper, opts.XATPKeeper, opts.FeeMarketKeeper, opts.EvmKeeper), // Check eth effective gas price against the global MinGasPrice
evmante.NewEthValidateBasicDecorator(opts.EvmKeeper),
evmante.NewEthSigVerificationDecorator(opts.EvmKeeper),
evmante.NewEthAccountVerificationDecorator(opts.AccountKeeper, opts.EvmKeeper),
Expand Down
222 changes: 191 additions & 31 deletions ante/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/types"
tmstrings "github.com/tendermint/tendermint/libs/strings"

ethermintante "github.com/evmos/ethermint/app/ante"
xplatypes "github.com/xpladev/xpla/types"

xatpkeeper "github.com/xpladev/xpla/x/xatp/keeper"
)

const maxBypassMinFeeMsgGasUsage = uint64(200_000)
Expand All @@ -17,65 +22,100 @@ const maxBypassMinFeeMsgGasUsage = uint64(200_000)
// is rejected. This applies for both CheckTx and DeliverTx
// If fee is high enough, then call next AnteHandler
// CONTRACT: Tx must implement FeeTx to use MinGasPriceDecorator
type MinGasPriceDecorator struct {
type MempoolFeeDecorator struct {
BypassMinFeeMsgTypes []string

feesKeeper ethermintante.FeeMarketKeeper
evmKeeper ethermintante.EVMKeeper
accountKeeper authante.AccountKeeper
xatpKeeper xatpkeeper.Keeper
feesKeeper ethermintante.FeeMarketKeeper
evmKeeper ethermintante.EVMKeeper
}

func NewMinGasPriceDecorator(fk ethermintante.FeeMarketKeeper, ek ethermintante.EVMKeeper, bypassMsgTypes []string) MinGasPriceDecorator {
return MinGasPriceDecorator{feesKeeper: fk, evmKeeper: ek, BypassMinFeeMsgTypes: bypassMsgTypes}
func NewMempoolFeeDecorator(bypassMsgTypes []string, ak authante.AccountKeeper, ck xatpkeeper.Keeper, fk ethermintante.FeeMarketKeeper, ek ethermintante.EVMKeeper) MempoolFeeDecorator {
return MempoolFeeDecorator{
BypassMinFeeMsgTypes: bypassMsgTypes,
accountKeeper: ak,
xatpKeeper: ck,
feesKeeper: fk,
evmKeeper: ek,
}
}

func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
func (mfd MempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}

minGasPrice := mpd.feesKeeper.GetParams(ctx).MinGasPrice
minGasPrice := mfd.feesKeeper.GetParams(ctx).MinGasPrice
gas := feeTx.GetGas()
msgs := feeTx.GetMsgs()
feeCoins := feeTx.GetFee()

// Short-circuit if min gas price is 0 or if simulating
if minGasPrice.IsZero() || simulate || (mpd.bypassMinFeeMsgs(msgs) && gas <= uint64(len(msgs))*maxBypassMinFeeMsgGasUsage) {
return next(ctx, tx, simulate)
}

evmDenom := mpd.evmKeeper.GetParams(ctx).EvmDenom
evmDenom := mfd.evmKeeper.GetParams(ctx).EvmDenom
minGasPrices := sdk.DecCoins{
{
Denom: evmDenom,
Amount: minGasPrice,
},
}

feeCoins := feeTx.GetFee()

requiredFees := make(sdk.Coins, 0)

// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
gasLimit := sdk.NewDecFromBigInt(new(big.Int).SetUint64(gas))

for _, gp := range minGasPrices {
fee := gp.Amount.Mul(gasLimit).Ceil().RoundInt()
if fee.IsPositive() {
requiredFees = requiredFees.Add(sdk.Coin{Denom: gp.Denom, Amount: fee})
// Only check for minimum fees if the execution mode is CheckTx and the tx does
// not contain operator configured bypass messages. If the tx does contain
// operator configured bypass messages only, it's total gas must be less than
// or equal to a constant, otherwise minimum fees are checked to prevent spam.
if ctx.IsCheckTx() && !(mfd.bypassMinFeeMsgs(msgs) && gas <= uint64(len(msgs))*maxBypassMinFeeMsgGasUsage) {
mempoolCheckGas := ctx.GasMeter().GasConsumed()

if !minGasPrices.IsZero() {
var defaultGasPrice sdk.DecCoin
for _, minGasPrice := range minGasPrices {
if minGasPrice.Denom == xplatypes.DefaultDenom {
defaultGasPrice = minGasPrice
taxRate := mfd.xatpKeeper.GetTaxRate(ctx)
defaultGasPrice.Amount = defaultGasPrice.Amount.Mul(sdk.OneDec().Add(taxRate))
break
}
}

for _, fee := range feeCoins {
xatp, found := mfd.xatpKeeper.GetXatp(ctx, fee.Denom)
if found {
ratioDec, err := mfd.xatpKeeper.GetFeeInfoFromXATP(ctx, xatp.Denom)
if err != nil {
return ctx, err
}

decimalDiff := sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(sdk.Precision-int64(xatp.Decimals)), nil))
minGasPrices = minGasPrices.Add(sdk.NewDecCoinFromDec(xatp.Denom, defaultGasPrice.Amount.Mul(ratioDec).QuoInt(decimalDiff)))
}
}

requiredFees := make(sdk.Coins, len(minGasPrices))

// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
glDec := sdk.NewDec(int64(gas))
var fee sdk.Dec
for i, gp := range minGasPrices {
fee = gp.Amount.Mul(glDec)

requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
}

if !simulate && !feeCoins.IsAnyGTE(requiredFees) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
}
}
}

if !feeCoins.IsAnyGTE(requiredFees) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "provided fee < minimum global fee (%s < %s). Please increase the gas price.", feeCoins, requiredFees)
ctx.GasMeter().RefundGas(ctx.GasMeter().GasConsumed()-mempoolCheckGas, "refund mempool check")
}

return next(ctx, tx, simulate)
}

func (mpd MinGasPriceDecorator) bypassMinFeeMsgs(msgs []sdk.Msg) bool {
func (mfd MempoolFeeDecorator) bypassMinFeeMsgs(msgs []sdk.Msg) bool {
for _, msg := range msgs {
if tmstrings.StringInSlice(sdk.MsgTypeURL(msg), mpd.BypassMinFeeMsgTypes) {
if tmstrings.StringInSlice(sdk.MsgTypeURL(msg), mfd.BypassMinFeeMsgTypes) {
continue
}

Expand All @@ -84,3 +124,123 @@ func (mpd MinGasPriceDecorator) bypassMinFeeMsgs(msgs []sdk.Msg) bool {

return true
}

type DeductFeeDecorator struct {
accountKeeper authante.AccountKeeper
bankKeeper types.BankKeeper
feegrantKeeper authante.FeegrantKeeper

xatpKeeper xatpkeeper.Keeper
}

func NewDeductFeeDecorator(ak authante.AccountKeeper, bk types.BankKeeper, fk authante.FeegrantKeeper, xk xatpkeeper.Keeper) DeductFeeDecorator {
return DeductFeeDecorator{
accountKeeper: ak,
bankKeeper: bk,
feegrantKeeper: fk,

xatpKeeper: xk,
}
}

func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}

fee := feeTx.GetFee()
feePayer := feeTx.FeePayer()
feeGranter := feeTx.FeeGranter()

deductFeesFrom := feePayer

// if feegranter set deduct fee from feegranter account.
// this works with only when feegrant enabled.
if feeGranter != nil {
if dfd.feegrantKeeper == nil {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not enabled")
} else if !feeGranter.Equals(feePayer) {
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs())

if err != nil {
return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer)
}
}

deductFeesFrom = feeGranter
}

deductFeesFromAcc := dfd.accountKeeper.GetAccount(ctx, deductFeesFrom)
if deductFeesFromAcc == nil {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom)
}

// deduct the fees
if feeTx.GetFee().Len() > 0 {
nativeFees := sdk.Coins{}
xatpFees := sdk.Coins{}

for _, fee := range feeTx.GetFee() {
xatp, found := dfd.xatpKeeper.GetXatp(ctx, fee.Denom)
if !found {
nativeFees = nativeFees.Add(fee)
continue
}

if simulate && feeTx.GetFee().IsZero() {
// for gas auto, add to minimum amount
fee.Amount = sdk.OneInt()
}

err := dfd.xatpKeeper.PayXATP(ctx, deductFeesFrom, xatp.Denom, fee.Amount.String())
if err != nil {
return ctx, err
}

ratioDec, err := dfd.xatpKeeper.GetFeeInfoFromXATP(ctx, xatp.Denom)
if err != nil {
return ctx, err
}

feeAmount := sdk.NewDecFromIntWithPrec(fee.Amount, int64(xatp.Decimals))
defaultFeeAmountDec := feeAmount.Quo(ratioDec)
xatpFees = xatpFees.Add(sdk.NewCoin(xplatypes.DefaultDenom, defaultFeeAmountDec.MulInt(sdk.DefaultPowerReduction).TruncateInt()))
}

if !nativeFees.Empty() {
err = DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, nativeFees)
if err != nil {
return ctx, err
}
}

if !xatpFees.Empty() {
err = dfd.xatpKeeper.DeductAndDistiributeFees(ctx, xatpFees)
if err != nil {
return ctx, err
}
}
}

events := sdk.Events{sdk.NewEvent(sdk.EventTypeTx,
sdk.NewAttribute(sdk.AttributeKeyFee, feeTx.GetFee().String()),
)}

ctx.EventManager().EmitEvents(events)

return next(ctx, tx, simulate)
}

func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins) error {
if !fees.IsValid() {
return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
}

err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees)
if err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
}

return nil
}
20 changes: 10 additions & 10 deletions ante/fee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
ibcclienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
ibcchanneltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"

"github.com/xpladev/xpla/ante"
"github.com/xpladev/xpla/types"
)

func (s *IntegrationTestSuite) TestMinGasPriceDecorator() {
s.SetupTest()
s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder()

s.app.FeeMarketKeeper.SetParams(s.ctx, feemarkettypes.NewParams(true, 8, 2, 0, 0, sdk.NewDec(200), sdk.MustNewDecFromStr("1.5")))
s.app.EvmKeeper.SetParams(s.ctx, evmtypes.NewParams(types.DefaultDenom, true, true, evmtypes.DefaultChainConfig()))

mpd := ante.NewMinGasPriceDecorator(
s.app.FeeMarketKeeper,
s.app.EvmKeeper,
[]string{
sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}),
sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}),
sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}),
})
antehandler := sdk.ChainAnteDecorators(mpd)
mfd := ante.NewMempoolFeeDecorator([]string{
sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}),
sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}),
sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}),
}, s.app.AccountKeeper, s.app.XATPKeeper, s.app.FeeMarketKeeper, s.app.EvmKeeper)
antehandler := sdk.ChainAnteDecorators(mfd)
priv1, _, addr1 := testdata.KeyTestPubAddr()

msg := testdata.NewTestMsg(addr1)
Expand All @@ -40,7 +40,7 @@ func (s *IntegrationTestSuite) TestMinGasPriceDecorator() {
s.Require().NoError(err)

// Set high gas price so standard test fee fails
feeAmt := sdk.NewDecCoinFromDec("uatom", sdk.NewDec(200).Quo(sdk.NewDec(100000)))
feeAmt := sdk.NewDecCoinFromDec(types.DefaultDenom, sdk.NewDec(200).Quo(sdk.NewDec(100000)))
minGasPrice := []sdk.DecCoin{feeAmt}
s.ctx = s.ctx.WithMinGasPrices(minGasPrice).WithIsCheckTx(true)

Expand Down
Loading