From ed0ad82ff969fc7446b68d00e1f7a276ddda7f00 Mon Sep 17 00:00:00 2001 From: Anh Minh <1phamminh0811@gmail.com> Date: Tue, 16 Jul 2024 16:49:47 +0700 Subject: [PATCH] Allow to paid by multiple fees denom --- custom/auth/ante/ante.go | 8 +-- custom/auth/ante/expected_keeper.go | 1 + custom/wasm/keeper/handler_plugin.go | 20 +++--- go.mod | 4 +- go.sum | 8 +-- x/tax2gas/ante/ante.go | 84 ++++++++++++++--------- x/tax2gas/keeper/expected_keeper.go | 41 ------------ x/tax2gas/keeper/keeper.go | 2 +- x/tax2gas/post/post.go | 99 +++++++++++++++++----------- x/tax2gas/types/errors.go | 7 +- x/tax2gas/types/expected_keeper.go | 7 ++ x/tax2gas/types/keys.go | 4 +- x/tax2gas/types/params.go | 18 ----- x/tax2gas/utils/fee_tax.go | 20 +----- x/tax2gas/utils/utils.go | 9 +++ 15 files changed, 158 insertions(+), 174 deletions(-) delete mode 100644 x/tax2gas/keeper/expected_keeper.go diff --git a/custom/auth/ante/ante.go b/custom/auth/ante/ante.go index 52610155d..ddfcde150 100644 --- a/custom/auth/ante/ante.go +++ b/custom/auth/ante/ante.go @@ -13,8 +13,8 @@ import ( dyncommante "github.com/classic-terra/core/v3/x/dyncomm/ante" dyncommkeeper "github.com/classic-terra/core/v3/x/dyncomm/keeper" tax2gasante "github.com/classic-terra/core/v3/x/tax2gas/ante" - - tax2gasKeeper "github.com/classic-terra/core/v3/x/tax2gas/keeper" + tax2gaskeeper "github.com/classic-terra/core/v3/x/tax2gas/keeper" + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" "github.com/cosmos/cosmos-sdk/codec" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" @@ -29,7 +29,7 @@ type HandlerOptions struct { AccountKeeper ante.AccountKeeper BankKeeper BankKeeper ExtensionOptionChecker ante.ExtensionOptionChecker - FeegrantKeeper ante.FeegrantKeeper + FeegrantKeeper tax2gastypes.FeegrantKeeper OracleKeeper OracleKeeper TreasuryKeeper TreasuryKeeper SignModeHandler signing.SignModeHandler @@ -43,7 +43,7 @@ type HandlerOptions struct { TXCounterStoreKey storetypes.StoreKey DyncommKeeper dyncommkeeper.Keeper StakingKeeper *stakingkeeper.Keeper - Tax2Gaskeeper tax2gasKeeper.Keeper + Tax2Gaskeeper tax2gaskeeper.Keeper Cdc codec.BinaryCodec } diff --git a/custom/auth/ante/expected_keeper.go b/custom/auth/ante/expected_keeper.go index 2bb206534..0e82e7126 100644 --- a/custom/auth/ante/expected_keeper.go +++ b/custom/auth/ante/expected_keeper.go @@ -25,6 +25,7 @@ type OracleKeeper interface { // BankKeeper defines the contract needed for supply related APIs (noalias) type BankKeeper interface { + GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error SendCoins(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error diff --git a/custom/wasm/keeper/handler_plugin.go b/custom/wasm/keeper/handler_plugin.go index 09aaba50e..f120e36f1 100644 --- a/custom/wasm/keeper/handler_plugin.go +++ b/custom/wasm/keeper/handler_plugin.go @@ -4,12 +4,7 @@ import ( "regexp" "strings" - marketexported "github.com/classic-terra/core/v3/x/market/exported" - "github.com/classic-terra/core/v3/x/tax2gas/types" - treasurykeeper "github.com/classic-terra/core/v3/x/treasury/keeper" - errorsmod "cosmossdk.io/errors" - wasmvmtypes "github.com/CosmWasm/wasmvm/types" "github.com/cosmos/cosmos-sdk/baseapp" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -22,6 +17,11 @@ import ( wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + + marketexported "github.com/classic-terra/core/v3/x/market/exported" + "github.com/classic-terra/core/v3/x/tax2gas/types" + treasurykeeper "github.com/classic-terra/core/v3/x/treasury/keeper" ) // msgEncoder is an extension point to customize encodings @@ -83,10 +83,10 @@ func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddr return nil, nil, err } - tx_origin := ctx.Value(wasmtypes.TxOrigin) - feePayer, ok := tx_origin.(sdk.AccAddress) + txOrigin := ctx.Value(wasmtypes.TxOrigin) + feePayer, ok := txOrigin.(sdk.AccAddress) if !ok { - return nil, nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, "tx_origin is not a valid") + return nil, nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, "tx_origin is not valid") } for _, sdkMsg := range sdkMsgs { taxes := FilterMsgAndComputeTax(ctx, h.treasuryKeeper, sdkMsg) @@ -124,7 +124,7 @@ func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Ad // make sure this account can send it for _, acct := range msg.GetSigners() { if !acct.Equals(contractAddr) { - return nil, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission") + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "contract doesn't have permission") } } @@ -139,7 +139,7 @@ func (h SDKMessageHandler) handleSdkMessage(ctx sdk.Context, contractAddr sdk.Ad // proto messages and has registered all `Msg services`, then this // path should never be called, because all those Msgs should be // registered within the `msgServiceRouter` already. - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg) + return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg) } var IBCRegexp = regexp.MustCompile("^ibc/[a-fA-F0-9]{64}$") diff --git a/go.mod b/go.mod index 992398a07..4130d6653 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( cosmossdk.io/math v1.3.0 cosmossdk.io/simapp v0.0.0-20230602123434-616841b9704d github.com/CosmWasm/wasmd v0.45.0 - github.com/CosmWasm/wasmvm v1.5.0 + github.com/CosmWasm/wasmvm v1.5.2 github.com/cometbft/cometbft v0.37.4 github.com/cometbft/cometbft-db v0.8.0 github.com/cosmos/cosmos-sdk v0.47.10 @@ -225,7 +225,7 @@ replace ( ) replace ( - github.com/CosmWasm/wasmd => github.com/classic-terra/wasmd v0.45.0-terra.3 + github.com/CosmWasm/wasmd => github.com/classic-terra/wasmd v0.45.1-0.20240716075902-ce9c9cb9b363 // use cometbft github.com/cometbft/cometbft => github.com/classic-terra/cometbft v0.37.4-terra1 github.com/cometbft/cometbft-db => github.com/cometbft/cometbft-db v0.8.0 diff --git a/go.sum b/go.sum index d5e71c43c..6ca77d484 100644 --- a/go.sum +++ b/go.sum @@ -222,8 +222,8 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg6 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRrLeDnvGIM= github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= -github.com/CosmWasm/wasmvm v1.5.0 h1:3hKeT9SfwfLhxTGKH3vXaKFzBz1yuvP8SlfwfQXbQfw= -github.com/CosmWasm/wasmvm v1.5.0/go.mod h1:fXB+m2gyh4v9839zlIXdMZGeLAxqUdYdFQqYsTha2hc= +github.com/CosmWasm/wasmvm v1.5.2 h1:+pKB1Mz9GZVt1vadxB+EDdD1FOz3dMNjIKq/58/lrag= +github.com/CosmWasm/wasmvm v1.5.2/go.mod h1:Q0bSEtlktzh7W2hhEaifrFp1Erx11ckQZmjq8FLCyys= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= @@ -356,8 +356,8 @@ github.com/classic-terra/goleveldb v0.0.0-20230914223247-2b28f6655121 h1:fjpWDB0 github.com/classic-terra/goleveldb v0.0.0-20230914223247-2b28f6655121/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/classic-terra/ibc-go/v7 v7.4.0-terra h1:hawaq62XKlxyc8xLyIcc6IujDDEbqDBU+2U15SF+hj8= github.com/classic-terra/ibc-go/v7 v7.4.0-terra/go.mod h1:s0lxNkjVIqsb8AVltL0qhzxeLgOKvWZrknPuvgjlEQ8= -github.com/classic-terra/wasmd v0.45.0-terra.3 h1:Fpjrjco0ig4dLwX6KrT+z0Zjo2iJCggM2X0JppyA/ko= -github.com/classic-terra/wasmd v0.45.0-terra.3/go.mod h1:pTPOut260rZ3J0WROheOgNWDV/vk/AeRdXmM1JOtMjk= +github.com/classic-terra/wasmd v0.45.1-0.20240716075902-ce9c9cb9b363 h1:z6qheMvuS9Uongc5obQQbL0hmiJ7s5MKHfYgJl4BQeU= +github.com/classic-terra/wasmd v0.45.1-0.20240716075902-ce9c9cb9b363/go.mod h1:cdb31ecSxIQrq/ZmYf9KYNbsdqbHfFUUGiA2dHeYxsA= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= diff --git a/x/tax2gas/ante/ante.go b/x/tax2gas/ante/ante.go index 8c1d60a8a..619d3b307 100644 --- a/x/tax2gas/ante/ante.go +++ b/x/tax2gas/ante/ante.go @@ -12,7 +12,7 @@ import ( tax2gasKeeper "github.com/classic-terra/core/v3/x/tax2gas/keeper" "github.com/classic-terra/core/v3/x/tax2gas/types" - tax2gasUtils "github.com/classic-terra/core/v3/x/tax2gas/utils" + tax2gasutils "github.com/classic-terra/core/v3/x/tax2gas/utils" ) // FeeDecorator deducts fees from the first signer of the tx @@ -22,12 +22,12 @@ import ( type FeeDecorator struct { accountKeeper ante.AccountKeeper bankKeeper types.BankKeeper - feegrantKeeper ante.FeegrantKeeper + feegrantKeeper types.FeegrantKeeper treasuryKeeper types.TreasuryKeeper tax2gasKeeper tax2gasKeeper.Keeper } -func NewFeeDecorator(ak ante.AccountKeeper, bk types.BankKeeper, fk ante.FeegrantKeeper, tk types.TreasuryKeeper, taxKeeper tax2gasKeeper.Keeper) FeeDecorator { +func NewFeeDecorator(ak ante.AccountKeeper, bk types.BankKeeper, fk types.FeegrantKeeper, tk types.TreasuryKeeper, taxKeeper tax2gasKeeper.Keeper) FeeDecorator { return FeeDecorator{ accountKeeper: ak, bankKeeper: bk, @@ -53,22 +53,22 @@ func (fd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, nex ) msgs := feeTx.GetMsgs() - if tax2gasUtils.IsOracleTx(msgs) { + if tax2gasutils.IsOracleTx(msgs) { return next(ctx, tx, simulate) } // Compute taxes based on consumed gas gasPrices := fd.tax2gasKeeper.GetGasPrices(ctx) gasConsumed := ctx.GasMeter().GasConsumed() - gasConsumedTax, err := tax2gasUtils.ComputeTaxOnGasConsumed(ctx, tx, gasPrices, gasConsumed) + gasConsumedFees, err := tax2gasutils.ComputeFeesOnGasConsumed(ctx, tx, gasPrices, gasConsumed) if err != nil { return ctx, err } // Compute taxes based on sent amount - taxes := tax2gasUtils.FilterMsgAndComputeTax(ctx, fd.treasuryKeeper, msgs...) + taxes := tax2gasutils.FilterMsgAndComputeTax(ctx, fd.treasuryKeeper, msgs...) // Convert taxes to gas - taxGas, err := tax2gasUtils.ComputeGas(ctx, tx, gasPrices, taxes) + taxGas, err := tax2gasutils.ComputeGas(ctx, tx, gasPrices, taxes) if err != nil { return ctx, err } @@ -78,15 +78,15 @@ func (fd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, nex } if !simulate && !taxes.IsZero() { - // Fee has to at least be enough to cover taxes + gasConsumedTax - priority, err = fd.checkTxFee(ctx, tx, gasConsumedTax.Add(taxes...)) + // Fee has to at least be enough to cover taxes + gasConsumedFees + priority, err = fd.checkTxFee(ctx, tx, gasConsumedFees.Add(taxes...)) if err != nil { return ctx, err } } - // Try to deduct the gasConsumed tax - feeDenom, err := fd.checkDeductFee(ctx, feeTx, gasConsumedTax, simulate) + // Try to deduct the gasConsumed fees + paidDenom, err := fd.checkDeductFee(ctx, feeTx, gasConsumedFees, simulate) if err != nil { return ctx, err } @@ -95,12 +95,11 @@ func (fd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, nex if taxGas != 0 { newCtx = newCtx.WithValue(types.TaxGas, taxGas) } - if !gasConsumedTax.IsZero() { - newCtx = newCtx.WithValue(types.ConsumedGasFee, gasConsumedTax) - } - if feeDenom != "" { - newCtx = newCtx.WithValue(types.FeeDenom, feeDenom) + newCtx = newCtx.WithValue(types.AnteConsumedGas, gasConsumed) + if paidDenom != "" { + newCtx = newCtx.WithValue(types.PaidDenom, paidDenom) } + return next(newCtx, tx, simulate) } @@ -114,8 +113,7 @@ func (fd FeeDecorator) checkDeductFee(ctx sdk.Context, feeTx sdk.FeeTx, taxes sd feeGranter := feeTx.FeeGranter() deductFeesFrom := feePayer - var foundCoins sdk.Coins - + foundCoins := sdk.Coins{} if !taxes.IsZero() { for _, coin := range feeCoins { found, requiredFee := taxes.Find(coin.Denom) @@ -123,7 +121,7 @@ func (fd FeeDecorator) checkDeductFee(ctx sdk.Context, feeTx sdk.FeeTx, taxes sd continue } if coin.Amount.GT(requiredFee.Amount) { - foundCoins = sdk.NewCoins(requiredFee) + foundCoins = foundCoins.Add(requiredFee) } } } else { @@ -136,9 +134,27 @@ func (fd FeeDecorator) checkDeductFee(ctx sdk.Context, feeTx sdk.FeeTx, taxes sd if fd.feegrantKeeper == nil { return "", sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled") } else if !feeGranter.Equals(feePayer) { - err := fd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, foundCoins, feeTx.GetMsgs()) + allowance, err := fd.feegrantKeeper.GetAllowance(ctx, feeGranter, feePayer) if err != nil { - return "", errorsmod.Wrapf(err, "%s does not not allow to pay fees for %s", feeGranter, feePayer) + return "", errorsmod.Wrapf(err, "fee-grant not found with granter %s and grantee %s", feeGranter, feePayer) + } + + granted := false + for _, foundCoin := range foundCoins { + _, err := allowance.Accept(ctx, sdk.NewCoins(foundCoin), feeTx.GetMsgs()) + if err == nil { + foundCoins = sdk.NewCoins(foundCoin) + granted = true + err = fd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, sdk.NewCoins(foundCoin), feeTx.GetMsgs()) + if err != nil { + return "", errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) + } + break + } + } + + if !granted { + return "", errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) } } @@ -152,11 +168,11 @@ func (fd FeeDecorator) checkDeductFee(ctx sdk.Context, feeTx sdk.FeeTx, taxes sd // deduct the fees if !foundCoins.IsZero() { - err := DeductFees(fd.bankKeeper, ctx, deductFeesFromAcc, foundCoins) + foundCoins, err := DeductFees(fd.bankKeeper, ctx, deductFeesFromAcc, foundCoins) if err != nil { return "", err } - if !foundCoins.IsZero() && !simulate { + if !simulate { err := fd.BurnTaxSplit(ctx, foundCoins) if err != nil { return "", err @@ -183,17 +199,23 @@ func (fd FeeDecorator) checkDeductFee(ctx sdk.Context, feeTx sdk.FeeTx, taxes sd } // DeductFees deducts fees from the given account. -func DeductFees(bankKeeper authtypes.BankKeeper, ctx sdk.Context, acc authtypes.AccountI, fees sdk.Coins) error { +func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc authtypes.AccountI, fees sdk.Coins) (sdk.Coins, error) { if !fees.IsValid() { - return errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) + return nil, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) } - err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), authtypes.FeeCollectorName, fees) - if err != nil { - return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + for _, fee := range fees { + balance := bankKeeper.GetBalance(ctx, acc.GetAddress(), fee.Denom) + if balance.IsGTE(fee) { + err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), authtypes.FeeCollectorName, sdk.NewCoins(fee)) + if err != nil { + return nil, errorsmod.Wrapf(err, "failed to send fee to fee collector: %s", fee) + } + return sdk.NewCoins(fee), nil + } } - return nil + return nil, sdkerrors.ErrInsufficientFunds } // checkTxFee implements the default fee logic, where the minimum price per @@ -209,7 +231,7 @@ func (fd FeeDecorator) checkTxFee(ctx sdk.Context, tx sdk.Tx, taxes sdk.Coins) ( feeCoins := feeTx.GetFee() gas := feeTx.GetGas() msgs := feeTx.GetMsgs() - isOracleTx := tax2gasUtils.IsOracleTx(msgs) + isOracleTx := tax2gasutils.IsOracleTx(msgs) gasPrices := fd.tax2gasKeeper.GetGasPrices(ctx) // Ensure that the provided fees meet a minimum threshold for the validator, @@ -234,7 +256,7 @@ func (fd FeeDecorator) checkTxFee(ctx sdk.Context, tx sdk.Tx, taxes sdk.Coins) ( priority := int64(math.MaxInt64) if !isOracleTx { - priority = tax2gasUtils.GetTxPriority(feeCoins, int64(gas)) + priority = tax2gasutils.GetTxPriority(feeCoins, int64(gas)) } return priority, nil diff --git a/x/tax2gas/keeper/expected_keeper.go b/x/tax2gas/keeper/expected_keeper.go deleted file mode 100644 index 58af418ae..000000000 --- a/x/tax2gas/keeper/expected_keeper.go +++ /dev/null @@ -1,41 +0,0 @@ -package keeper - -import ( - "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" - distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" -) - -// TreasuryKeeper for tax charging & recording -type TreasuryKeeper interface { - RecordEpochTaxProceeds(ctx sdk.Context, delta sdk.Coins) - GetTaxRate(ctx sdk.Context) (taxRate sdk.Dec) - GetTaxCap(ctx sdk.Context, denom string) (taxCap math.Int) - GetBurnSplitRate(ctx sdk.Context) sdk.Dec - HasBurnTaxExemptionAddress(ctx sdk.Context, addresses ...string) bool - HasBurnTaxExemptionContract(ctx sdk.Context, address string) bool - GetMinInitialDepositRatio(ctx sdk.Context) sdk.Dec -} - -// OracleKeeper for feeder validation -type OracleKeeper interface { - ValidateFeeder(ctx sdk.Context, feederAddr sdk.AccAddress, validatorAddr sdk.ValAddress) error -} - -// BankKeeper defines the contract needed for supply related APIs (noalias) -type BankKeeper interface { - IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error - SendCoins(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error -} - -type DistrKeeper interface { - FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error - GetFeePool(ctx sdk.Context) distributiontypes.FeePool -} - -type GovKeeper interface { - GetDepositParams(ctx sdk.Context) govv1.DepositParams -} diff --git a/x/tax2gas/keeper/keeper.go b/x/tax2gas/keeper/keeper.go index 2ae8e3926..799363108 100644 --- a/x/tax2gas/keeper/keeper.go +++ b/x/tax2gas/keeper/keeper.go @@ -61,5 +61,5 @@ func (k Keeper) GetAuthority() string { } func (k Keeper) GetGasPrices(ctx sdk.Context) sdk.DecCoins { - return k.GetParams(ctx).GasPrices + return k.GetParams(ctx).GasPrices.Sort() } diff --git a/x/tax2gas/post/post.go b/x/tax2gas/post/post.go index 148886a4c..a637ca207 100644 --- a/x/tax2gas/post/post.go +++ b/x/tax2gas/post/post.go @@ -9,7 +9,7 @@ import ( tax2gasKeeper "github.com/classic-terra/core/v3/x/tax2gas/keeper" "github.com/classic-terra/core/v3/x/tax2gas/types" - tax2gasUtils "github.com/classic-terra/core/v3/x/tax2gas/utils" + tax2gasutils "github.com/classic-terra/core/v3/x/tax2gas/utils" ) type Tax2gasPostDecorator struct { @@ -39,58 +39,77 @@ func (dd Tax2gasPostDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate b } feeCoins := feeTx.GetFee() - paidFees := ctx.Value(types.ConsumedGasFee) - paidFeeCoins, ok := paidFees.(sdk.Coins) + anteConsumedGas, ok := ctx.Value(types.AnteConsumedGas).(uint64) if !ok { - paidFeeCoins = sdk.NewCoins() + return ctx, errorsmod.Wrap(types.ErrParsing, "Error parsing ante consumed gas") } - taxGas, ok := ctx.Value(types.TaxGas).(uint64) - if !ok { - taxGas = 0 - } - // we consume the gas here as we need to calculate the tax for consumed gas - ctx.GasMeter().ConsumeGas(taxGas, "tax gas") - gasConsumed := ctx.GasMeter().GasConsumed() - - gasPrices := dd.tax2gasKeeper.GetGasPrices(ctx) - gasConsumedTax, err := tax2gasUtils.ComputeTaxOnGasConsumed(ctx, tx, gasPrices, gasConsumed) - if err != nil { - return ctx, err - } - - if simulate { - return next(ctx, tx, simulate, success) - } - - var requiredFees sdk.Coins - if gasConsumedTax != nil && feeCoins != nil { - // Remove the paid fee coins in ante handler - requiredFees = gasConsumedTax.Sub(paidFeeCoins...) - - // Check if fee coins contains at least one coin that can cover required fees - if !feeCoins.IsAnyGTE(requiredFees) { - return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %q, required: %q", feeCoins, requiredFees) - } - - // Get fee denom identified in ante handler - feeDenom, ok := ctx.Value(types.FeeDenom).(string) + if !feeCoins.IsZero() { + // Get paid denom identified in ante handler + paidDenom, ok := ctx.Value(types.PaidDenom).(string) if !ok { return ctx, errorsmod.Wrap(types.ErrParsing, "Error parsing fee denom") } - found, requiredFee := requiredFees.Find(feeDenom) + gasPrices := dd.tax2gasKeeper.GetGasPrices(ctx) + paidDenomGasPrice, found := tax2gasutils.GetGasPriceByDenom(ctx, gasPrices, paidDenom) if !found { - return ctx, errorsmod.Wrapf(types.ErrCoinNotFound, "can'f find %s in %s", feeDenom, requiredFees) + return ctx, types.ErrDenomNotFound } + paidAmount := paidDenomGasPrice.Mul(sdk.NewDec(int64(anteConsumedGas))) + // Deduct feeCoins with paid amount + feeCoins = feeCoins.Sub(sdk.NewCoin(paidDenom, paidAmount.Ceil().RoundInt())) - feePayer := dd.accountKeeper.GetAccount(ctx, feeTx.FeePayer()) + taxGas, ok := ctx.Value(types.TaxGas).(uint64) + if !ok { + taxGas = 0 + } + // we consume the gas here as we need to calculate the tax for consumed gas + ctx.GasMeter().ConsumeGas(taxGas, "tax gas") + gasConsumed := ctx.GasMeter().GasConsumed() - err := dd.bankKeeper.SendCoinsFromAccountToModule(ctx, feePayer.GetAddress(), authtypes.FeeCollectorName, sdk.NewCoins(requiredFee)) + // Deduct the gas consumed amount spent on ante handler + gasRemaining := gasConsumed - anteConsumedGas + gasRemainingFees, err := tax2gasutils.ComputeFeesOnGasConsumed(ctx, tx, gasPrices, gasRemaining) if err != nil { - return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + return ctx, err } - } + if simulate { + return next(ctx, tx, simulate, success) + } + + for _, feeCoin := range feeCoins { + found, feeRequired := gasRemainingFees.Find(feeCoin.Denom) + if !found { + continue + } + + feePayer := dd.accountKeeper.GetAccount(ctx, feeTx.FeePayer()) + if feeCoin.IsGTE(feeRequired) { + err := dd.bankKeeper.SendCoinsFromAccountToModule(ctx, feePayer.GetAddress(), authtypes.FeeCollectorName, sdk.NewCoins(feeRequired)) + if err != nil { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + } + gasRemaining = 0 + break + } else { + gasPrice, found := tax2gasutils.GetGasPriceByDenom(ctx, gasPrices, feeCoin.Denom) + if !found { + continue + } + + err := dd.bankKeeper.SendCoinsFromAccountToModule(ctx, feePayer.GetAddress(), authtypes.FeeCollectorName, sdk.NewCoins(feeCoin)) + if err != nil { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + } + feeRemaining := sdk.NewDecCoinFromCoin(feeRequired.Sub(feeCoin)) + gasRemaining = uint64(feeRemaining.Amount.Quo(gasPrice).Ceil().RoundInt64()) + } + } + if gasRemaining > 0 { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "fees are not enough to pay for gas") + } + } return next(ctx, tx, simulate, success) } diff --git a/x/tax2gas/types/errors.go b/x/tax2gas/types/errors.go index 04e40e3f4..501df004d 100644 --- a/x/tax2gas/types/errors.go +++ b/x/tax2gas/types/errors.go @@ -4,8 +4,9 @@ import ( errorsmod "cosmossdk.io/errors" ) -// Market errors +// Tax2Gas errors var ( - ErrParsing = errorsmod.Register(ModuleName, 1, "Parsing errors") - ErrCoinNotFound = errorsmod.Register(ModuleName, 2, "Coin not found") + ErrParsing = errorsmod.Register(ModuleName, 1, "Parsing errors") + ErrCoinNotFound = errorsmod.Register(ModuleName, 2, "Coin not found") + ErrDenomNotFound = errorsmod.Register(ModuleName, 3, "Denom not found") ) diff --git a/x/tax2gas/types/expected_keeper.go b/x/tax2gas/types/expected_keeper.go index 8fbab7047..9281fec9c 100644 --- a/x/tax2gas/types/expected_keeper.go +++ b/x/tax2gas/types/expected_keeper.go @@ -2,6 +2,7 @@ package types import ( "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/x/feegrant" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -18,9 +19,15 @@ type TreasuryKeeper interface { // BankKeeper defines the contract needed for supply related APIs (noalias) type BankKeeper interface { + GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error SendCoins(ctx sdk.Context, from, to sdk.AccAddress, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error } + +type FeegrantKeeper interface { + GetAllowance(ctx sdk.Context, granter, grantee sdk.AccAddress) (feegrant.FeeAllowanceI, error) + UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error +} diff --git a/x/tax2gas/types/keys.go b/x/tax2gas/types/keys.go index 1ee2b1bbb..0e1052e73 100644 --- a/x/tax2gas/types/keys.go +++ b/x/tax2gas/types/keys.go @@ -7,11 +7,11 @@ const ( RouterKey = ModuleName - ConsumedGasFee = "consumedGasFee" + AnteConsumedGas = "anteConsumedGas" TaxGas = "taxGas" - FeeDenom = "feeDenom" + PaidDenom = "paidDenom" ) // Key defines the store key for tax2gas. diff --git a/x/tax2gas/types/params.go b/x/tax2gas/types/params.go index 71404001c..1ced415fd 100644 --- a/x/tax2gas/types/params.go +++ b/x/tax2gas/types/params.go @@ -4,19 +4,8 @@ import ( fmt "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) -// Parameter store keys. -var ( - KeyParamField = []byte("TODO: CHANGE ME") -) - -// ParamTable for tax2gas module. -func ParamKeyTable() paramtypes.KeyTable { - return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) -} - func NewParams() Params { return Params{} } @@ -59,10 +48,3 @@ func (p Params) Validate() error { } return nil } - -// Implements params.ParamSet. -func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { - return paramtypes.ParamSetPairs{ - // paramtypes.NewParamSetPair(KeyParamField, &p.Field, validateFn), - } -} diff --git a/x/tax2gas/utils/fee_tax.go b/x/tax2gas/utils/fee_tax.go index 762f653ed..1c795fb80 100644 --- a/x/tax2gas/utils/fee_tax.go +++ b/x/tax2gas/utils/fee_tax.go @@ -13,7 +13,6 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" marketexported "github.com/classic-terra/core/v3/x/market/exported" - oracleexported "github.com/classic-terra/core/v3/x/oracle/exported" "github.com/classic-terra/core/v3/x/tax2gas/types" ) @@ -145,13 +144,13 @@ func ComputeGas(ctx sdk.Context, tx sdk.Tx, gasPrices sdk.DecCoins, taxes sdk.Co return tax2gas.Uint64(), nil } -func ComputeTaxOnGasConsumed(ctx sdk.Context, tx sdk.Tx, gasPrices sdk.DecCoins, gas uint64) (sdk.Coins, error) { +func ComputeFeesOnGasConsumed(ctx sdk.Context, tx sdk.Tx, gasPrices sdk.DecCoins, gas uint64) (sdk.Coins, error) { feeTx, ok := tx.(sdk.FeeTx) if !ok { return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") } - isOracleTx := isOracleTx(feeTx.GetMsgs()) + isOracleTx := IsOracleTx(feeTx.GetMsgs()) gasFees := make(sdk.Coins, len(gasPrices)) if !isOracleTx && len(gasPrices) != 0 { @@ -166,18 +165,3 @@ func ComputeTaxOnGasConsumed(ctx sdk.Context, tx sdk.Tx, gasPrices sdk.DecCoins, return gasFees, nil } - -func isOracleTx(msgs []sdk.Msg) bool { - for _, msg := range msgs { - switch msg.(type) { - case *oracleexported.MsgAggregateExchangeRatePrevote: - continue - case *oracleexported.MsgAggregateExchangeRateVote: - continue - default: - return false - } - } - - return true -} diff --git a/x/tax2gas/utils/utils.go b/x/tax2gas/utils/utils.go index 7a73feaf6..6a96b917e 100644 --- a/x/tax2gas/utils/utils.go +++ b/x/tax2gas/utils/utils.go @@ -42,3 +42,12 @@ func IsOracleTx(msgs []sdk.Msg) bool { return true } + +func GetGasPriceByDenom(ctx sdk.Context, gasPrices sdk.DecCoins, denom string) (sdk.Dec, bool) { + for _, price := range gasPrices { + if price.Denom == denom { + return price.Amount, true + } + } + return sdk.Dec{}, false +}