diff --git a/app/app.go b/app/app.go index 823d41c85..feebb451e 100644 --- a/app/app.go +++ b/app/app.go @@ -56,6 +56,7 @@ import ( v7_1 "github.com/classic-terra/core/v3/app/upgrades/v7_1" v8 "github.com/classic-terra/core/v3/app/upgrades/v8" v8_1 "github.com/classic-terra/core/v3/app/upgrades/v8_1" + v9 "github.com/classic-terra/core/v3/app/upgrades/v9" customante "github.com/classic-terra/core/v3/custom/auth/ante" custompost "github.com/classic-terra/core/v3/custom/auth/post" @@ -87,6 +88,7 @@ var ( v7_1.Upgrade, v8.Upgrade, v8_1.Upgrade, + v9.Upgrade, } // Forks defines forks to be applied to the network @@ -240,6 +242,7 @@ func NewTerraApp( TXCounterStoreKey: app.GetKey(wasmtypes.StoreKey), DyncommKeeper: app.DyncommKeeper, StakingKeeper: app.StakingKeeper, + Tax2Gaskeeper: app.Tax2gasKeeper, Cdc: app.appCodec, }, ) @@ -249,7 +252,13 @@ func NewTerraApp( postHandler, err := custompost.NewPostHandler( custompost.HandlerOptions{ - DyncommKeeper: app.DyncommKeeper, + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + FeegrantKeeper: app.FeeGrantKeeper, + DistrKeeper: app.DistrKeeper, + DyncommKeeper: app.DyncommKeeper, + TreasuryKeeper: app.TreasuryKeeper, + Tax2Gaskeeper: app.Tax2gasKeeper, }, ) if err != nil { @@ -398,7 +407,7 @@ func (app *TerraApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.APIC // RegisterTxService implements the Application.RegisterTxService method. func (app *TerraApp) RegisterTxService(clientCtx client.Context) { authtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.BaseApp.Simulate, app.interfaceRegistry) - customauthtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.TreasuryKeeper) + customauthtx.RegisterTxService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.TreasuryKeeper, app.Tax2gasKeeper) } // RegisterTendermintService implements the Application.RegisterTendermintService method. diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 406fd28c9..8f7ffee7e 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -66,6 +66,8 @@ import ( markettypes "github.com/classic-terra/core/v3/x/market/types" oraclekeeper "github.com/classic-terra/core/v3/x/oracle/keeper" oracletypes "github.com/classic-terra/core/v3/x/oracle/types" + tax2gasKeeper "github.com/classic-terra/core/v3/x/tax2gas/keeper" + tax2gasTypes "github.com/classic-terra/core/v3/x/tax2gas/types" treasurykeeper "github.com/classic-terra/core/v3/x/treasury/keeper" treasurytypes "github.com/classic-terra/core/v3/x/treasury/types" ) @@ -103,10 +105,10 @@ type AppKeepers struct { DyncommKeeper dyncommkeeper.Keeper IBCHooksKeeper *ibchookskeeper.Keeper ConsensusParamsKeeper consensusparamkeeper.Keeper - - Ics20WasmHooks *ibchooks.WasmHooks - IBCHooksWrapper *ibchooks.ICS4Middleware - TransferStack ibctransfer.IBCModule + Tax2gasKeeper tax2gasKeeper.Keeper + Ics20WasmHooks *ibchooks.WasmHooks + IBCHooksWrapper *ibchooks.ICS4Middleware + TransferStack ibctransfer.IBCModule // make scoped keepers public for test purposes ScopedIBCKeeper capabilitykeeper.ScopedKeeper @@ -156,6 +158,7 @@ func NewAppKeepers( treasurytypes.StoreKey, wasmtypes.StoreKey, dyncommtypes.StoreKey, + tax2gasTypes.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -276,6 +279,12 @@ func NewAppKeepers( stakingtypes.NewMultiStakingHooks(appKeepers.DistrKeeper.Hooks(), appKeepers.SlashingKeeper.Hooks()), ) + appKeepers.Tax2gasKeeper = tax2gasKeeper.NewKeeper( + appCodec, + appKeepers.keys[tax2gasTypes.StoreKey], + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + // Create IBC Keeper appKeepers.IBCKeeper = ibckeeper.NewKeeper( appCodec, @@ -390,6 +399,7 @@ func NewAppKeepers( appKeepers.BankKeeper, appKeepers.TreasuryKeeper, appKeepers.AccountKeeper, + appKeepers.Tax2gasKeeper, appCodec, appKeepers.TransferKeeper, ) diff --git a/app/modules.go b/app/modules.go index a7dfb5cb2..f638fc110 100644 --- a/app/modules.go +++ b/app/modules.go @@ -25,6 +25,8 @@ import ( markettypes "github.com/classic-terra/core/v3/x/market/types" "github.com/classic-terra/core/v3/x/oracle" oracletypes "github.com/classic-terra/core/v3/x/oracle/types" + tax2gas "github.com/classic-terra/core/v3/x/tax2gas" + tax2gasTypes "github.com/classic-terra/core/v3/x/tax2gas/types" "github.com/classic-terra/core/v3/x/treasury" treasuryclient "github.com/classic-terra/core/v3/x/treasury/client" treasurytypes "github.com/classic-terra/core/v3/x/treasury/types" @@ -123,6 +125,7 @@ var ( customwasm.AppModuleBasic{}, ibcfee.AppModuleBasic{}, dyncomm.AppModuleBasic{}, + tax2gas.AppModuleBasic{}, ibchooks.AppModuleBasic{}, consensus.AppModuleBasic{}, ) @@ -184,6 +187,7 @@ func appModules( treasury.NewAppModule(appCodec, app.TreasuryKeeper), wasm.NewAppModule(appCodec, &app.WasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.MsgServiceRouter(), app.GetSubspace(wasmtypes.ModuleName)), dyncomm.NewAppModule(appCodec, app.DyncommKeeper, app.StakingKeeper), + tax2gas.NewAppModule(appCodec, app.Tax2gasKeeper), ibchooks.NewAppModule(app.AccountKeeper), consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper), crisis.NewAppModule(app.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), // always be last to make sure that it checks for all invariants and not only part of them @@ -250,6 +254,7 @@ func orderBeginBlockers() []string { markettypes.ModuleName, wasmtypes.ModuleName, dyncommtypes.ModuleName, + tax2gasTypes.ModuleName, // consensus module consensusparamtypes.ModuleName, } @@ -284,6 +289,7 @@ func orderEndBlockers() []string { markettypes.ModuleName, wasmtypes.ModuleName, dyncommtypes.ModuleName, + tax2gasTypes.ModuleName, // consensus module consensusparamtypes.ModuleName, } @@ -318,6 +324,7 @@ func orderInitGenesis() []string { treasurytypes.ModuleName, wasmtypes.ModuleName, dyncommtypes.ModuleName, + tax2gasTypes.ModuleName, // consensus module consensusparamtypes.ModuleName, } diff --git a/app/sim_test.go b/app/sim_test.go index 144b73f0a..a3904a5e2 100644 --- a/app/sim_test.go +++ b/app/sim_test.go @@ -21,10 +21,13 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/simulation" simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli" + + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" ) // SimAppChainID hardcoded chainID for simulation @@ -142,16 +145,33 @@ func TestAppStateDeterminism(t *testing.T) { simtestutil.EmptyAppOptions{}, emptyWasmOpts, interBlockCacheOpt(), fauxMerkleModeOpt(), ) + appGenState := app.DefaultGenesis() + tax2gasGenState := tax2gastypes.GenesisState{} + err := app.AppCodec().UnmarshalJSON(appGenState[tax2gastypes.ModuleName], &tax2gasGenState) + require.NoError(t, err) + + tax2gasGenState.Params = tax2gastypes.Params{ + Enabled: true, + GasPrices: sdk.DecCoins{ + sdk.NewDecCoinFromDec("stake", sdk.NewDecWithPrec(0, 3)), + }, + } + newGenState := tax2gasGenState + bz, err := app.AppCodec().MarshalJSON(&newGenState) + require.NoError(t, err) + + appGenState[tax2gastypes.ModuleName] = bz + fmt.Printf( "running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed, ) - _, _, err := simulation.SimulateFromSeed( + _, _, err = simulation.SimulateFromSeed( t, os.Stdout, app.BaseApp, - AppStateFn(app.AppCodec(), app.SimulationManager(), app.DefaultGenesis()), + AppStateFn(app.AppCodec(), app.SimulationManager(), appGenState), simtypes.RandomAccounts, // Replace with own random account function if using keys other than secp256k1 simtestutil.SimulationOperations(app, app.AppCodec(), config), app.ModuleAccountAddrs(), diff --git a/app/upgrades/v9/constants.go b/app/upgrades/v9/constants.go new file mode 100644 index 000000000..8bca8b61c --- /dev/null +++ b/app/upgrades/v9/constants.go @@ -0,0 +1,20 @@ +package v9 + +import ( + "github.com/classic-terra/core/v3/app/upgrades" + store "github.com/cosmos/cosmos-sdk/store/types" + + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +const UpgradeName = "v9" + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: CreateV9UpgradeHandler, + StoreUpgrades: store.StoreUpgrades{ + Added: []string{ + tax2gastypes.ModuleName, + }, + }, +} diff --git a/app/upgrades/v9/upgrades.go b/app/upgrades/v9/upgrades.go new file mode 100644 index 000000000..fb67fa7dc --- /dev/null +++ b/app/upgrades/v9/upgrades.go @@ -0,0 +1,31 @@ +package v9 + +import ( + "github.com/classic-terra/core/v3/app/keepers" + "github.com/classic-terra/core/v3/app/upgrades" + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +func CreateV9UpgradeHandler( + mm *module.Manager, + cfg module.Configurator, + _ upgrades.BaseAppParamManager, + keepers *keepers.AppKeepers, +) upgradetypes.UpgradeHandler { + return func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // set default oracle split + keepers.TreasuryKeeper.SetTaxRate(ctx, sdk.ZeroDec()) + + tax2gasParams := tax2gastypes.DefaultParams() + tax2gasParams.GasPrices = sdk.NewDecCoins( + sdk.NewDecCoinFromDec("uluna", sdk.NewDecWithPrec(28325, 3)), + sdk.NewDecCoinFromDec("uusd", sdk.NewDecWithPrec(75, 2)), + ) + tax2gasParams.MaxTotalBypassMinFeeMsgGasUsage = 200000 + keepers.Tax2gasKeeper.SetParams(ctx, tax2gasParams) + return mm.RunMigrations(ctx, cfg, fromVM) + } +} diff --git a/contrib/updates/prepare_cosmovisor.sh b/contrib/updates/prepare_cosmovisor.sh index 703be14a9..17d983443 100644 --- a/contrib/updates/prepare_cosmovisor.sh +++ b/contrib/updates/prepare_cosmovisor.sh @@ -5,7 +5,7 @@ # These fields should be fetched automatically in the future # Need to do more upgrade to see upgrade patterns -OLD_VERSION=v3.0.3 +OLD_VERSION=v3.1.3 # this command will retrieve the folder with the largest number in format v SOFTWARE_UPGRADE_NAME=$(ls -d -- ./app/upgrades/v* | sort -Vr | head -n 1 | xargs basename) BUILDDIR=$1 diff --git a/custom/auth/ante/ante.go b/custom/auth/ante/ante.go index 75400c615..ddfcde150 100644 --- a/custom/auth/ante/ante.go +++ b/custom/auth/ante/ante.go @@ -12,6 +12,9 @@ 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" + 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" @@ -26,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 @@ -40,6 +43,7 @@ type HandlerOptions struct { TXCounterStoreKey storetypes.StoreKey DyncommKeeper dyncommkeeper.Keeper StakingKeeper *stakingkeeper.Keeper + Tax2Gaskeeper tax2gaskeeper.Keeper Cdc codec.BinaryCodec } @@ -88,7 +92,7 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) { // MinInitialDepositDecorator prevents submitting governance proposal low initial deposit NewMinInitialDepositDecorator(options.GovKeeper, options.TreasuryKeeper), ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), - NewFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TreasuryKeeper, options.DistributionKeeper), + tax2gasante.NewFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TreasuryKeeper, options.Tax2Gaskeeper), dyncommante.NewDyncommDecorator(options.Cdc, options.DyncommKeeper, options.StakingKeeper), // Do not add any other decorators below this point unless explicitly explain. diff --git a/custom/auth/ante/expected_keeper.go b/custom/auth/ante/expected_keeper.go index 05957c631..002a1f804 100644 --- a/custom/auth/ante/expected_keeper.go +++ b/custom/auth/ante/expected_keeper.go @@ -26,10 +26,12 @@ 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 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 DistrKeeper interface { @@ -42,3 +44,7 @@ type DistrKeeper interface { type GovKeeper interface { GetDepositParams(ctx sdk.Context) govv1.DepositParams } + +type Tax2GasKeeper interface { + GetBurnTaxRate(ctx sdk.Context) (burnTaxRate sdk.Dec) +} diff --git a/custom/auth/ante/fee.go b/custom/auth/ante/fee.go deleted file mode 100644 index 8e8cf402c..000000000 --- a/custom/auth/ante/fee.go +++ /dev/null @@ -1,219 +0,0 @@ -package ante - -import ( - "fmt" - "math" - - errorsmod "cosmossdk.io/errors" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -// FeeDecorator deducts fees from the first signer of the tx -// If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error -// Call next AnteHandler if fees successfully deducted -// CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator -type FeeDecorator struct { - accountKeeper ante.AccountKeeper - bankKeeper BankKeeper - feegrantKeeper ante.FeegrantKeeper - treasuryKeeper TreasuryKeeper - distrKeeper DistrKeeper -} - -func NewFeeDecorator(ak ante.AccountKeeper, bk BankKeeper, fk ante.FeegrantKeeper, tk TreasuryKeeper, dk DistrKeeper) FeeDecorator { - return FeeDecorator{ - accountKeeper: ak, - bankKeeper: bk, - feegrantKeeper: fk, - treasuryKeeper: tk, - distrKeeper: dk, - } -} - -func (fd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { - return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") - } - - if !simulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 { - return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas") - } - - var ( - priority int64 - err error - ) - - msgs := feeTx.GetMsgs() - // Compute taxes - taxes := FilterMsgAndComputeTax(ctx, fd.treasuryKeeper, msgs...) - - if !simulate { - priority, err = fd.checkTxFee(ctx, tx, taxes) - if err != nil { - return ctx, err - } - } - - if err := fd.checkDeductFee(ctx, feeTx, taxes, simulate); err != nil { - return ctx, err - } - - newCtx := ctx.WithPriority(priority) - - return next(newCtx, tx, simulate) -} - -func (fd FeeDecorator) checkDeductFee(ctx sdk.Context, feeTx sdk.FeeTx, taxes sdk.Coins, simulate bool) error { - if addr := fd.accountKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil { - return fmt.Errorf("fee collector module account (%s) has not been set", types.FeeCollectorName) - } - - 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 fd.feegrantKeeper == nil { - return sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled") - } else if !feeGranter.Equals(feePayer) { - err := fd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, feeTx.GetMsgs()) - if err != nil { - return errorsmod.Wrapf(err, "%s does not not allow to pay fees for %s", feeGranter, feePayer) - } - } - - deductFeesFrom = feeGranter - } - - deductFeesFromAcc := fd.accountKeeper.GetAccount(ctx, deductFeesFrom) - if deductFeesFromAcc == nil { - return sdkerrors.ErrUnknownAddress.Wrapf("fee payer address: %s does not exist", deductFeesFrom) - } - - feesOrTax := fee - - // deduct the fees - if fee.IsZero() && simulate { - feesOrTax = taxes - } - - if !feesOrTax.IsZero() { - err := DeductFees(fd.bankKeeper, ctx, deductFeesFromAcc, feesOrTax) - if err != nil { - return err - } - - if !taxes.IsZero() { - err := fd.BurnTaxSplit(ctx, taxes) - if err != nil { - return err - } - // Record tax proceeds - fd.treasuryKeeper.RecordEpochTaxProceeds(ctx, taxes) - } - } - - events := sdk.Events{ - sdk.NewEvent( - sdk.EventTypeTx, - sdk.NewAttribute(sdk.AttributeKeyFee, fee.String()), - sdk.NewAttribute(sdk.AttributeKeyFeePayer, deductFeesFrom.String()), - ), - } - ctx.EventManager().EmitEvents(events) - - return nil -} - -// DeductFees deducts fees from the given account. -func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins) error { - if !fees.IsValid() { - return errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) - } - - err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees) - if err != nil { - return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) - } - - return nil -} - -// checkTxFee implements the default fee logic, where the minimum price per -// unit of gas is fixed and set by each validator, can the tx priority is computed from the gas price. -// Transaction with only oracle messages will skip gas fee check and will have the most priority. -// It also checks enough fee for treasury tax -func (fd FeeDecorator) checkTxFee(ctx sdk.Context, tx sdk.Tx, taxes sdk.Coins) (int64, error) { - feeTx, ok := tx.(sdk.FeeTx) - if !ok { - return 0, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") - } - - feeCoins := feeTx.GetFee() - gas := feeTx.GetGas() - msgs := feeTx.GetMsgs() - isOracleTx := isOracleTx(msgs) - - // Ensure that the provided fees meet a minimum threshold for the validator, - // if this is a CheckTx. This is only for local mempool purposes, and thus - // is only ran on check tx. - if ctx.IsCheckTx() && !isOracleTx { - requiredGasFees := sdk.Coins{} - minGasPrices := ctx.MinGasPrices() - if !minGasPrices.IsZero() { - requiredGasFees = 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)) - for i, gp := range minGasPrices { - fee := gp.Amount.Mul(glDec) - requiredGasFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) - } - } - - requiredFees := requiredGasFees.Add(taxes...) - - // Check required fees - if !requiredFees.IsZero() && !feeCoins.IsAnyGTE(requiredFees) { - return 0, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %q, required: %q = %q(gas) + %q(stability)", feeCoins, requiredFees, requiredGasFees, taxes) - } - } - - priority := int64(math.MaxInt64) - - if !isOracleTx { - priority = getTxPriority(feeCoins, int64(gas)) - } - - return priority, nil -} - -// getTxPriority returns a naive tx priority based on the amount of the smallest denomination of the gas price -// provided in a transaction. -// NOTE: This implementation should be used with a great consideration as it opens potential attack vectors -// where txs with multiple coins could not be prioritize as expected. -func getTxPriority(fee sdk.Coins, gas int64) int64 { - var priority int64 - for _, c := range fee { - p := int64(math.MaxInt64) - gasPrice := c.Amount.QuoRaw(gas) - if gasPrice.IsInt64() { - p = gasPrice.Int64() - } - if priority == 0 || p < priority { - priority = p - } - } - - return priority -} diff --git a/custom/auth/ante/fee_tax.go b/custom/auth/ante/fee_tax.go deleted file mode 100644 index 96a0e48d6..000000000 --- a/custom/auth/ante/fee_tax.go +++ /dev/null @@ -1,128 +0,0 @@ -package ante - -import ( - "regexp" - "strings" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - sdk "github.com/cosmos/cosmos-sdk/types" - authz "github.com/cosmos/cosmos-sdk/x/authz" - 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" -) - -var IBCRegexp = regexp.MustCompile("^ibc/[a-fA-F0-9]{64}$") - -func isIBCDenom(denom string) bool { - return IBCRegexp.MatchString(strings.ToLower(denom)) -} - -// FilterMsgAndComputeTax computes the stability tax on messages. -func FilterMsgAndComputeTax(ctx sdk.Context, tk TreasuryKeeper, msgs ...sdk.Msg) sdk.Coins { - taxes := sdk.Coins{} - - for _, msg := range msgs { - switch msg := msg.(type) { - case *banktypes.MsgSend: - if !tk.HasBurnTaxExemptionAddress(ctx, msg.FromAddress, msg.ToAddress) { - taxes = taxes.Add(computeTax(ctx, tk, msg.Amount)...) - } - - case *banktypes.MsgMultiSend: - tainted := 0 - - for _, input := range msg.Inputs { - if tk.HasBurnTaxExemptionAddress(ctx, input.Address) { - tainted++ - } - } - - for _, output := range msg.Outputs { - if tk.HasBurnTaxExemptionAddress(ctx, output.Address) { - tainted++ - } - } - - if tainted != len(msg.Inputs)+len(msg.Outputs) { - for _, input := range msg.Inputs { - taxes = taxes.Add(computeTax(ctx, tk, input.Coins)...) - } - } - - case *marketexported.MsgSwapSend: - taxes = taxes.Add(computeTax(ctx, tk, sdk.NewCoins(msg.OfferCoin))...) - - case *wasmtypes.MsgInstantiateContract: - taxes = taxes.Add(computeTax(ctx, tk, msg.Funds)...) - - case *wasmtypes.MsgInstantiateContract2: - taxes = taxes.Add(computeTax(ctx, tk, msg.Funds)...) - - case *wasmtypes.MsgExecuteContract: - if !tk.HasBurnTaxExemptionContract(ctx, msg.Contract) { - taxes = taxes.Add(computeTax(ctx, tk, msg.Funds)...) - } - - case *authz.MsgExec: - messages, err := msg.GetMessages() - if err == nil { - taxes = taxes.Add(FilterMsgAndComputeTax(ctx, tk, messages...)...) - } - } - } - - return taxes -} - -// computes the stability tax according to tax-rate and tax-cap -func computeTax(ctx sdk.Context, tk TreasuryKeeper, principal sdk.Coins) sdk.Coins { - taxRate := tk.GetTaxRate(ctx) - if taxRate.Equal(sdk.ZeroDec()) { - return sdk.Coins{} - } - - taxes := sdk.Coins{} - - for _, coin := range principal { - if coin.Denom == sdk.DefaultBondDenom { - continue - } - - if isIBCDenom(coin.Denom) { - continue - } - - taxDue := sdk.NewDecFromInt(coin.Amount).Mul(taxRate).TruncateInt() - - // If tax due is greater than the tax cap, cap! - taxCap := tk.GetTaxCap(ctx, coin.Denom) - if taxDue.GT(taxCap) { - taxDue = taxCap - } - - if taxDue.Equal(sdk.ZeroInt()) { - continue - } - - taxes = taxes.Add(sdk.NewCoin(coin.Denom, taxDue)) - } - - return taxes -} - -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/custom/auth/ante/fee_test.go b/custom/auth/ante/fee_test.go deleted file mode 100644 index ecd8a634f..000000000 --- a/custom/auth/ante/fee_test.go +++ /dev/null @@ -1,1005 +0,0 @@ -package ante_test - -import ( - "encoding/json" - "fmt" - "os" - "time" - - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - authz "github.com/cosmos/cosmos-sdk/x/authz" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - "github.com/classic-terra/core/v3/custom/auth/ante" - core "github.com/classic-terra/core/v3/types" - markettypes "github.com/classic-terra/core/v3/x/market/types" - oracletypes "github.com/classic-terra/core/v3/x/oracle/types" -) - -func (s *AnteTestSuite) TestDeductFeeDecorator_ZeroGas() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(300))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - msg := testdata.NewTestMsg(addr1) - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - - // set zero gas - s.txBuilder.SetGasLimit(0) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err) - - // zero gas is accepted in simulation mode - _, err = antehandler(s.ctx, tx, true) - s.Require().NoError(err) -} - -func (s *AnteTestSuite) TestEnsureMempoolFees() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(300))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - msg := testdata.NewTestMsg(addr1) - feeAmount := testdata.NewTestFeeAmount() - gasLimit := uint64(15) - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // Set high gas price so standard test fee fails - atomPrice := sdk.NewDecCoinFromDec("atom", sdk.NewDec(20)) - highGasPrice := []sdk.DecCoin{atomPrice} - s.ctx = s.ctx.WithMinGasPrices(highGasPrice) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees - _, err = antehandler(s.ctx, tx, false) - s.Require().NotNil(err, "Decorator should have errored on too low fee for local gasPrice") - - // antehandler should not error since we do not check minGasPrice in simulation mode - cacheCtx, _ := s.ctx.CacheContext() - _, err = antehandler(cacheCtx, tx, true) - s.Require().Nil(err, "Decorator should not have errored in simulation mode") - - // Set IsCheckTx to false - s.ctx = s.ctx.WithIsCheckTx(false) - - // antehandler should not error since we do not check minGasPrice in DeliverTx - _, err = antehandler(s.ctx, tx, false) - s.Require().Nil(err, "MempoolFeeDecorator returned error in DeliverTx") - - // Set IsCheckTx back to true for testing sufficient mempool fee - s.ctx = s.ctx.WithIsCheckTx(true) - - atomPrice = sdk.NewDecCoinFromDec("atom", sdk.NewDec(0).Quo(sdk.NewDec(100000))) - lowGasPrice := []sdk.DecCoin{atomPrice} - s.ctx = s.ctx.WithMinGasPrices(lowGasPrice) - - newCtx, err := antehandler(s.ctx, tx, false) - s.Require().Nil(err, "Decorator should not have errored on fee higher than local gasPrice") - // Priority is the smallest gas price amount in any denom. Since we have only 1 gas price - // of 10atom, the priority here is 10. - s.Require().Equal(int64(10), newCtx.Priority()) -} - -func (s *AnteTestSuite) TestDeductFees() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - - // msg and signatures - msg := testdata.NewTestMsg(addr1) - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // Set account with insufficient funds - acc := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, addr1) - s.app.AccountKeeper.SetAccount(s.ctx, acc) - coins := sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(10))) - err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - s.Require().NoError(err) - - dfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(dfd) - - _, err = antehandler(s.ctx, tx, false) - - s.Require().NotNil(err, "Tx did not error when fee payer had insufficient funds") - - // Set account with sufficient funds - s.app.AccountKeeper.SetAccount(s.ctx, acc) - err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(200)))) - s.Require().NoError(err) - - _, err = antehandler(s.ctx, tx, false) - - s.Require().Nil(err, "Tx errored after account has been set with sufficient funds") -} - -func (s *AnteTestSuite) TestEnsureMempoolFeesSend() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - sendAmount := int64(1000000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) - msg := banktypes.NewMsgSend(addr1, addr1, sendCoins) - - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees due to tax - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax") - - tk := s.app.TreasuryKeeper - expectedTax := tk.GetTaxRate(s.ctx).MulInt64(sendAmount).TruncateInt() - if taxCap := tk.GetTaxCap(s.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) { - expectedTax = taxCap - } - - // set tax amount - feeAmount = sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax)) - s.txBuilder.SetFeeAmount(feeAmount) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // must pass with tax - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice") -} - -func (s *AnteTestSuite) TestEnsureMempoolFeesSwapSend() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - sendAmount := int64(1000000) - sendCoin := sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount) - msg := markettypes.NewMsgSwapSend(addr1, addr1, sendCoin, core.MicroKRWDenom) - - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees due to tax - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax") - - tk := s.app.TreasuryKeeper - expectedTax := tk.GetTaxRate(s.ctx).MulInt64(sendAmount).TruncateInt() - if taxCap := tk.GetTaxCap(s.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) { - expectedTax = taxCap - } - - // set tax amount - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax))) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // must pass with tax - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice") -} - -func (s *AnteTestSuite) TestEnsureMempoolFeesMultiSend() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - sendAmount := int64(1000000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) - msg := banktypes.NewMsgMultiSend( - []banktypes.Input{ - banktypes.NewInput(addr1, sendCoins), - banktypes.NewInput(addr1, sendCoins), - }, - []banktypes.Output{ - banktypes.NewOutput(addr1, sendCoins), - banktypes.NewOutput(addr1, sendCoins), - }, - ) - - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees due to tax - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax") - - tk := s.app.TreasuryKeeper - expectedTax := tk.GetTaxRate(s.ctx).MulInt64(sendAmount).TruncateInt() - if taxCap := tk.GetTaxCap(s.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) { - expectedTax = taxCap - } - - // set tax amount - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax))) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax") - - // must pass with tax - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax.Add(expectedTax)))) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice") -} - -func (s *AnteTestSuite) TestEnsureMempoolFeesInstantiateContract() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - sendAmount := int64(1000000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) - msg := &wasmtypes.MsgInstantiateContract{ - Sender: addr1.String(), - Admin: addr1.String(), - CodeID: 0, - Msg: []byte{}, - Funds: sendCoins, - } - - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees due to tax - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax") - - tk := s.app.TreasuryKeeper - expectedTax := tk.GetTaxRate(s.ctx).MulInt64(sendAmount).TruncateInt() - if taxCap := tk.GetTaxCap(s.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) { - expectedTax = taxCap - } - - // set tax amount - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax))) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // must pass with tax - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice") -} - -func (s *AnteTestSuite) TestEnsureMempoolFeesExecuteContract() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - sendAmount := int64(1000000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) - msg := &wasmtypes.MsgExecuteContract{ - Sender: addr1.String(), - Contract: addr1.String(), - Msg: []byte{}, - Funds: sendCoins, - } - - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees due to tax - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax") - - tk := s.app.TreasuryKeeper - expectedTax := tk.GetTaxRate(s.ctx).MulInt64(sendAmount).TruncateInt() - if taxCap := tk.GetTaxCap(s.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) { - expectedTax = taxCap - } - - // set tax amount - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax))) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // must pass with tax - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice") -} - -func (s *AnteTestSuite) TestEnsureMempoolFeesAuthzExec() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - sendAmount := int64(1000000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) - msg := authz.NewMsgExec(addr1, []sdk.Msg{banktypes.NewMsgSend(addr1, addr1, sendCoins)}) - - feeAmount := testdata.NewTestFeeAmount() - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(&msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // antehandler errors with insufficient fees due to tax - _, err = antehandler(s.ctx, tx, false) - s.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax") - - tk := s.app.TreasuryKeeper - expectedTax := tk.GetTaxRate(s.ctx).MulInt64(sendAmount).TruncateInt() - if taxCap := tk.GetTaxCap(s.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) { - expectedTax = taxCap - } - - // set tax amount - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax))) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // must pass with tax - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice") -} - -// go test -v -run ^TestAnteTestSuite/TestTaxExemption$ github.com/classic-terra/core/v3/custom/auth/ante -func (s *AnteTestSuite) TestTaxExemption() { - // keys and addresses - var privs []cryptotypes.PrivKey - var addrs []sdk.AccAddress - - // 0, 1: exemption - // 2, 3: normal - for i := 0; i < 4; i++ { - priv, _, addr := testdata.KeyTestPubAddr() - privs = append(privs, priv) - addrs = append(addrs, addr) - } - - // set send amount - sendAmt := int64(1000000) - sendCoin := sdk.NewInt64Coin(core.MicroSDRDenom, sendAmt) - feeAmt := int64(1000) - - cases := []struct { - name string - msgSigner cryptotypes.PrivKey - msgCreator func() []sdk.Msg - minFeeAmount int64 - expectProceeds int64 - }{ - { - name: "MsgSend(exemption -> exemption)", - msgSigner: privs[0], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - - return msgs - }, - minFeeAmount: 0, - expectProceeds: 0, - }, { - name: "MsgSend(normal -> normal)", - msgSigner: privs[2], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[2], addrs[3], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - - return msgs - }, - // tax this one hence burn amount is fee amount - minFeeAmount: feeAmt, - expectProceeds: feeAmt, - }, { - name: "MsgExec(MsgSend(normal -> normal))", - msgSigner: privs[2], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := authz.NewMsgExec(addrs[1], []sdk.Msg{banktypes.NewMsgSend(addrs[2], addrs[3], sdk.NewCoins(sendCoin))}) - msgs = append(msgs, &msg1) - - return msgs - }, - // tax this one hence burn amount is fee amount - minFeeAmount: feeAmt, - expectProceeds: feeAmt, - }, { - name: "MsgSend(exemption -> normal), MsgSend(exemption -> exemption)", - msgSigner: privs[0], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[0], addrs[2], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - msg2 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg2) - - return msgs - }, - // tax this one hence burn amount is fee amount - minFeeAmount: feeAmt, - expectProceeds: feeAmt, - }, { - name: "MsgSend(exemption -> exemption), MsgMultiSend(exemption -> normal, exemption -> exemption)", - msgSigner: privs[0], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - msg2 := banktypes.NewMsgMultiSend( - []banktypes.Input{ - { - Address: addrs[0].String(), - Coins: sdk.NewCoins(sendCoin), - }, - { - Address: addrs[0].String(), - Coins: sdk.NewCoins(sendCoin), - }, - }, - []banktypes.Output{ - { - Address: addrs[2].String(), - Coins: sdk.NewCoins(sendCoin), - }, - { - Address: addrs[1].String(), - Coins: sdk.NewCoins(sendCoin), - }, - }, - ) - msgs = append(msgs, msg2) - - return msgs - }, - minFeeAmount: feeAmt * 2, - expectProceeds: feeAmt * 2, - }, { - name: "MsgExecuteContract(exemption), MsgExecuteContract(normal)", - msgSigner: privs[3], - msgCreator: func() []sdk.Msg { - sendAmount := int64(1000000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) - // get wasm code for wasm contract create and instantiate - wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") - s.Require().NoError(err) - per := wasmkeeper.NewDefaultPermissionKeeper(s.app.WasmKeeper) - // set wasm default params - s.app.WasmKeeper.SetParams(s.ctx, wasmtypes.DefaultParams()) - // wasm create - CodeID, _, err := per.Create(s.ctx, addrs[0], wasmCode, nil) - s.Require().NoError(err) - // params for contract init - r := wasmkeeper.HackatomExampleInitMsg{Verifier: addrs[0], Beneficiary: addrs[0]} - bz, err := json.Marshal(r) - s.Require().NoError(err) - // change block time for contract instantiate - s.ctx = s.ctx.WithBlockTime(time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC)) - // instantiate contract then set the contract address to tax exemption - addr, _, err := per.Instantiate(s.ctx, CodeID, addrs[0], nil, bz, "my label", nil) - s.Require().NoError(err) - s.app.TreasuryKeeper.AddBurnTaxExemptionAddress(s.ctx, addr.String()) - // instantiate contract then not set to tax exemption - addr1, _, err := per.Instantiate(s.ctx, CodeID, addrs[0], nil, bz, "my label", nil) - s.Require().NoError(err) - - var msgs []sdk.Msg - // msg and signatures - msg1 := &wasmtypes.MsgExecuteContract{ - Sender: addrs[0].String(), - Contract: addr.String(), - Msg: []byte{}, - Funds: sendCoins, - } - msgs = append(msgs, msg1) - - msg2 := &wasmtypes.MsgExecuteContract{ - Sender: addrs[3].String(), - Contract: addr1.String(), - Msg: []byte{}, - Funds: sendCoins, - } - msgs = append(msgs, msg2) - return msgs - }, - minFeeAmount: feeAmt, - expectProceeds: feeAmt, - }, - } - - // there should be no coin in burn module - for _, c := range cases { - s.SetupTest(true) // setup - require := s.Require() - tk := s.app.TreasuryKeeper - ak := s.app.AccountKeeper - bk := s.app.BankKeeper - burnSplitRate := sdk.NewDecWithPrec(5, 1) - oracleSplitRate := sdk.ZeroDec() - - // Set burn split rate to 50% - // oracle split to 0% (oracle split is covered in another test) - tk.SetBurnSplitRate(s.ctx, burnSplitRate) - tk.SetOracleSplitRate(s.ctx, oracleSplitRate) - - fmt.Printf("CASE = %s \n", c.name) - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - tk.AddBurnTaxExemptionAddress(s.ctx, addrs[0].String()) - tk.AddBurnTaxExemptionAddress(s.ctx, addrs[1].String()) - - mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.DistrKeeper) - antehandler := sdk.ChainAnteDecorators(mfd) - - for i := 0; i < 4; i++ { - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(10000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addrs[i], coins) - } - - // msg and signatures - feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, c.minFeeAmount)) - gasLimit := testdata.NewTestGasLimit() - require.NoError(s.txBuilder.SetMsgs(c.msgCreator()...)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{c.msgSigner}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - require.NoError(err) - - _, err = antehandler(s.ctx, tx, false) - require.NoError(err) - - // check fee collector - feeCollector := ak.GetModuleAccount(s.ctx, authtypes.FeeCollectorName) - amountFee := bk.GetBalance(s.ctx, feeCollector.GetAddress(), core.MicroSDRDenom) - require.Equal(amountFee, sdk.NewCoin(core.MicroSDRDenom, sdk.NewDec(c.minFeeAmount).Mul(burnSplitRate).TruncateInt())) - - // check tax proceeds - taxProceeds := s.app.TreasuryKeeper.PeekEpochTaxProceeds(s.ctx) - require.Equal(taxProceeds, sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(c.expectProceeds)))) - } -} - -// go test -v -run ^TestAnteTestSuite/TestBurnSplitTax$ github.com/classic-terra/core/v3/custom/auth/ante -func (s *AnteTestSuite) TestBurnSplitTax() { - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 0), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 100% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 1), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 10% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 2), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 0.1% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(0, 0), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 0% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 0), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1)) // 100% distribute, 50% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1)) // 10% distribute, 50% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 2), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1)) // 0.1% distribute, 50% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(0, 0), sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1)) // 0% distribute, 50% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 0), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 100% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 1), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 10% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 2), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 0.1% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(0, 0), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // 0% distribute, 0% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 0), sdk.OneDec(), sdk.NewDecWithPrec(5, 1)) // 100% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 1), sdk.OneDec(), sdk.NewDecWithPrec(5, 1)) // 10% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 2), sdk.OneDec(), sdk.NewDecWithPrec(5, 1)) // 0.1% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(0, 0), sdk.OneDec(), sdk.NewDecWithPrec(5, 1)) // 0% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 2), sdk.OneDec(), sdk.NewDecWithPrec(5, 2)) // 0.1% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(0, 0), sdk.OneDec(), sdk.NewDecWithPrec(5, 2)) // 0% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(1, 2), sdk.OneDec(), sdk.NewDecWithPrec(1, 1)) // 0.1% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(0, 0), sdk.OneDec(), sdk.NewDecWithPrec(1, 2)) // 0% distribute, 100% to oracle - s.runBurnSplitTaxTest(sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec(), sdk.NewDecWithPrec(5, 1)) // -10% distribute - invalid rate -} - -func (s *AnteTestSuite) runBurnSplitTaxTest(burnSplitRate sdk.Dec, oracleSplitRate sdk.Dec, communityTax sdk.Dec) { - s.SetupTest(true) // setup - require := s.Require() - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - ak := s.app.AccountKeeper - bk := s.app.BankKeeper - tk := s.app.TreasuryKeeper - dk := s.app.DistrKeeper - mfd := ante.NewFeeDecorator(ak, bk, s.app.FeeGrantKeeper, tk, dk) - antehandler := sdk.ChainAnteDecorators(mfd) - - // Set burn split tax - tk.SetBurnSplitRate(s.ctx, burnSplitRate) - tk.SetOracleSplitRate(s.ctx, oracleSplitRate) - taxRate := tk.GetTaxRate(s.ctx) - - // Set community tax - dkParams := dk.GetParams(s.ctx) - dkParams.CommunityTax = communityTax - dk.SetParams(s.ctx, dkParams) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - coins := sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.NewInt(1000000))) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) - - // msg and signatures - sendAmount := int64(1000000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) - msg := banktypes.NewMsgSend(addr1, addr1, sendCoins) - - gasLimit := testdata.NewTestGasLimit() - require.NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetGasLimit(gasLimit) - expectedTax := tk.GetTaxRate(s.ctx).MulInt64(sendAmount).TruncateInt() - if taxCap := tk.GetTaxCap(s.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) { - expectedTax = taxCap - } - - // set tax amount - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedTax))) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - require.NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - feeCollector := ak.GetModuleAccount(s.ctx, authtypes.FeeCollectorName) - - amountFeeBefore := bk.GetAllBalances(s.ctx, feeCollector.GetAddress()) - - totalSupplyBefore, _, err := bk.GetPaginatedTotalSupply(s.ctx, &query.PageRequest{}) - require.NoError(err) - fmt.Printf( - "Before: TotalSupply %v, FeeCollector %v\n", - totalSupplyBefore, - amountFeeBefore, - ) - - // send tx to BurnTaxFeeDecorator antehandler - _, err = antehandler(s.ctx, tx, false) - require.NoError(err) - - // burn the burn account - tk.BurnCoinsFromBurnAccount(s.ctx) - - feeCollectorAfter := bk.GetAllBalances(s.ctx, ak.GetModuleAddress(authtypes.FeeCollectorName)) - oracleAfter := bk.GetAllBalances(s.ctx, ak.GetModuleAddress(oracletypes.ModuleName)) - taxes := ante.FilterMsgAndComputeTax(s.ctx, tk, msg) - communityPoolAfter, _ := dk.GetFeePoolCommunityCoins(s.ctx).TruncateDecimal() - if communityPoolAfter.IsZero() { - communityPoolAfter = sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, sdk.ZeroInt())) - } - - // burnTax := sdk.NewDecCoinsFromCoins(taxes...) - // in the burn tax split function, coins and not deccoins are used, which leads to rounding differences - // when comparing to the test with very small numbers, accordingly all deccoin calculations are changed to coins - burnTax := taxes - - if burnSplitRate.IsPositive() { - distributionDeltaCoins := burnSplitRate.MulInt(burnTax.AmountOf(core.MicroSDRDenom)).RoundInt() - applyCommunityTax := communityTax.Mul(oracleSplitRate.Quo(communityTax.Mul(oracleSplitRate).Sub(communityTax).Add(sdk.OneDec()))) - - expectedCommunityCoins := applyCommunityTax.MulInt(distributionDeltaCoins).RoundInt() - distributionDeltaCoins = distributionDeltaCoins.Sub(expectedCommunityCoins) - - expectedOracleCoins := oracleSplitRate.MulInt(distributionDeltaCoins).RoundInt() - expectedDistrCoins := distributionDeltaCoins.Sub(expectedOracleCoins) - - // expected: community pool 50% - fmt.Printf("-- sendCoins %+v, BurnTax %+v, BurnSplitRate %+v, OracleSplitRate %+v, CommunityTax %+v, CTaxApplied %+v, OracleCoins %+v, DistrCoins %+v\n", sendCoins.AmountOf(core.MicroSDRDenom), taxRate, burnSplitRate, oracleSplitRate, communityTax, applyCommunityTax, expectedOracleCoins, expectedDistrCoins) - require.Equal(feeCollectorAfter, sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedDistrCoins))) - require.Equal(oracleAfter, sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedOracleCoins))) - require.Equal(communityPoolAfter, sdk.NewCoins(sdk.NewCoin(core.MicroSDRDenom, expectedCommunityCoins))) - burnTax = burnTax.Sub(sdk.NewCoin(core.MicroSDRDenom, distributionDeltaCoins)).Sub(sdk.NewCoin(core.MicroSDRDenom, expectedCommunityCoins)) - } - - // check tax proceeds - // as end blocker has not been run here, we need to calculate it from the fee collector - addTaxFromFees := feeCollectorAfter.AmountOf(core.MicroSDRDenom) - if communityTax.IsPositive() { - addTaxFromFees = communityTax.Mul(sdk.NewDecFromInt(addTaxFromFees)).RoundInt() - } - expectedTaxProceeds := communityPoolAfter.AmountOf(core.MicroSDRDenom).Add(addTaxFromFees) - originalDistribution := sdk.ZeroDec() - if burnSplitRate.IsPositive() { - originalDistribution = burnSplitRate.Mul(sdk.NewDecFromInt(taxes.AmountOf(core.MicroSDRDenom))) - } - originalTaxProceeds := sdk.ZeroInt() - if communityTax.IsPositive() { - originalTaxProceeds = communityTax.Mul(originalDistribution).RoundInt() - } - // due to precision (roundInt) this can deviate up to 1 from the expected value - require.LessOrEqual(expectedTaxProceeds.Sub(originalTaxProceeds).Int64(), sdk.OneInt().Int64()) - - totalSupplyAfter, _, err := bk.GetPaginatedTotalSupply(s.ctx, &query.PageRequest{}) - require.NoError(err) - if !burnTax.Empty() { - // expected: total supply = tax - split tax - require.Equal( - totalSupplyBefore.Sub(totalSupplyAfter...), - burnTax, - ) - } - - fmt.Printf( - "After: TotalSupply %v, FeeCollector %v\n", - totalSupplyAfter, - feeCollectorAfter, - ) -} - -// go test -v -run ^TestAnteTestSuite/TestEnsureIBCUntaxed$ github.com/classic-terra/core/v3/custom/auth/ante -// TestEnsureIBCUntaxed tests that IBC transactions are not taxed, but fee is still deducted -func (s *AnteTestSuite) TestEnsureIBCUntaxed() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator( - s.app.AccountKeeper, - s.app.BankKeeper, - s.app.FeeGrantKeeper, - s.app.TreasuryKeeper, - s.app.DistrKeeper, - ) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - account := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, addr1) - s.app.AccountKeeper.SetAccount(s.ctx, account) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 1_000_000_000))) - - // msg and signatures - sendAmount := int64(1_000_000) - sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.OsmoIbcDenom, sendAmount)) - msg := banktypes.NewMsgSend(addr1, addr1, sendCoins) - - feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 1_000_000)) - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(msg)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - // Set IsCheckTx to true - s.ctx = s.ctx.WithIsCheckTx(true) - - // IBC must pass without burn - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err, "Decorator should not have errored on IBC denoms") - - // check if tax proceeds are empty - taxProceeds := s.app.TreasuryKeeper.PeekEpochTaxProceeds(s.ctx) - s.Require().True(taxProceeds.Empty()) -} - -// go test -v -run ^TestAnteTestSuite/TestOracleZeroFee$ github.com/classic-terra/core/v3/custom/auth/ante -func (s *AnteTestSuite) TestOracleZeroFee() { - s.SetupTest(true) // setup - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - mfd := ante.NewFeeDecorator( - s.app.AccountKeeper, - s.app.BankKeeper, - s.app.FeeGrantKeeper, - s.app.TreasuryKeeper, - s.app.DistrKeeper, - ) - antehandler := sdk.ChainAnteDecorators(mfd) - - // keys and addresses - priv1, _, addr1 := testdata.KeyTestPubAddr() - account := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, addr1) - s.app.AccountKeeper.SetAccount(s.ctx, account) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 1_000_000_000))) - - // new val - val, err := stakingtypes.NewValidator(sdk.ValAddress(addr1), priv1.PubKey(), stakingtypes.Description{}) - s.Require().NoError(err) - s.app.StakingKeeper.SetValidator(s.ctx, val) - - // msg and signatures - - // MsgAggregateExchangeRatePrevote - msg := oracletypes.NewMsgAggregateExchangeRatePrevote(oracletypes.GetAggregateVoteHash("salt", "exchange rates", val.GetOperator()), addr1, val.GetOperator()) - s.txBuilder.SetMsgs(msg) - s.txBuilder.SetGasLimit(testdata.NewTestGasLimit()) - s.txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 0))) - privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err) - - // check fee collector empty - balances := s.app.BankKeeper.GetAllBalances(s.ctx, s.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName)) - s.Require().Equal(sdk.Coins{}, balances) - - // MsgAggregateExchangeRateVote - msg1 := oracletypes.NewMsgAggregateExchangeRateVote("salt", "exchange rates", addr1, val.GetOperator()) - s.txBuilder.SetMsgs(msg1) - tx, err = s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - _, err = antehandler(s.ctx, tx, false) - s.Require().NoError(err) - - // check fee collector empty - balances = s.app.BankKeeper.GetAllBalances(s.ctx, s.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName)) - s.Require().Equal(sdk.Coins{}, balances) -} diff --git a/custom/auth/ante/integration_test.go b/custom/auth/ante/integration_test.go deleted file mode 100644 index ffadd5983..000000000 --- a/custom/auth/ante/integration_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package ante_test - -import ( - "fmt" - - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth/ante" - "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/bank/testutil" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - - customante "github.com/classic-terra/core/v3/custom/auth/ante" - core "github.com/classic-terra/core/v3/types" - treasurytypes "github.com/classic-terra/core/v3/x/treasury/types" -) - -// go test -v -run ^TestAnteTestSuite/TestIntegrationTaxExemption$ github.com/classic-terra/core/v3/custom/auth/ante -func (s *AnteTestSuite) TestIntegrationTaxExemption() { - // keys and addresses - var privs []cryptotypes.PrivKey - var addrs []sdk.AccAddress - - // 0, 1: exemption - // 2, 3: normal - for i := 0; i < 4; i++ { - priv, _, addr := testdata.KeyTestPubAddr() - privs = append(privs, priv) - addrs = append(addrs, addr) - } - - // set send amount - sendAmt := int64(1_000_000) - sendCoin := sdk.NewInt64Coin(core.MicroSDRDenom, sendAmt) - feeAmt := int64(1000) - - cases := []struct { - name string - msgSigner cryptotypes.PrivKey - msgCreator func() []sdk.Msg - expectedFeeAmount int64 - }{ - { - name: "MsgSend(exemption -> exemption)", - msgSigner: privs[0], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - - return msgs - }, - expectedFeeAmount: 0, - }, { - name: "MsgSend(normal -> normal)", - msgSigner: privs[2], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[2], addrs[3], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - - return msgs - }, - // tax this one hence burn amount is fee amount - expectedFeeAmount: feeAmt, - }, { - name: "MsgSend(exemption -> normal), MsgSend(exemption -> exemption)", - msgSigner: privs[0], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[0], addrs[2], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - msg2 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg2) - - return msgs - }, - // tax this one hence burn amount is fee amount - expectedFeeAmount: feeAmt, - }, { - name: "MsgSend(exemption -> exemption), MsgMultiSend(exemption -> normal, exemption)", - msgSigner: privs[0], - msgCreator: func() []sdk.Msg { - var msgs []sdk.Msg - - msg1 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) - msgs = append(msgs, msg1) - msg2 := banktypes.NewMsgMultiSend( - []banktypes.Input{ - { - Address: addrs[0].String(), - Coins: sdk.NewCoins(sendCoin.Add(sendCoin)), - }, - }, - []banktypes.Output{ - { - Address: addrs[2].String(), - Coins: sdk.NewCoins(sendCoin), - }, - { - Address: addrs[1].String(), - Coins: sdk.NewCoins(sendCoin), - }, - }, - ) - msgs = append(msgs, msg2) - - return msgs - }, - expectedFeeAmount: feeAmt * 2, - }, - } - - for _, c := range cases { - s.SetupTest(true) // setup - tk := s.app.TreasuryKeeper - ak := s.app.AccountKeeper - bk := s.app.BankKeeper - dk := s.app.DistrKeeper - wk := s.app.WasmKeeper - - // Set burn split rate to 50% - // fee amount should be 500, 50% of 10000 - burnSplitRate := sdk.NewDecWithPrec(5, 1) - tk.SetBurnSplitRate(s.ctx, burnSplitRate) // 50% - - feeCollector := ak.GetModuleAccount(s.ctx, types.FeeCollectorName) - burnModule := ak.GetModuleAccount(s.ctx, treasurytypes.BurnModuleName) - - encodingConfig := s.SetupEncoding() - wasmConfig := wasmtypes.DefaultWasmConfig() - antehandler, err := customante.NewAnteHandler( - customante.HandlerOptions{ - AccountKeeper: ak, - BankKeeper: bk, - WasmKeeper: &wk, - FeegrantKeeper: s.app.FeeGrantKeeper, - OracleKeeper: s.app.OracleKeeper, - TreasuryKeeper: s.app.TreasuryKeeper, - SigGasConsumer: ante.DefaultSigVerificationGasConsumer, - SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), - IBCKeeper: *s.app.IBCKeeper, - DistributionKeeper: dk, - WasmConfig: &wasmConfig, - TXCounterStoreKey: s.app.GetKey(wasmtypes.StoreKey), - }, - ) - s.Require().NoError(err) - - for i := 0; i < 4; i++ { - coins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, 1_000_000)) - testutil.FundAccount(s.app.BankKeeper, s.ctx, addrs[i], coins) - } - - s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() - - tk.AddBurnTaxExemptionAddress(s.ctx, addrs[0].String()) - tk.AddBurnTaxExemptionAddress(s.ctx, addrs[1].String()) - - s.Run(c.name, func() { - // case 1 provides zero fee so not enough fee - // case 2 provides enough fee - feeCases := []int64{0, feeAmt} - for i := 0; i < 1; i++ { - feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, feeCases[i])) - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError(s.txBuilder.SetMsgs(c.msgCreator()...)) - s.txBuilder.SetFeeAmount(feeAmount) - s.txBuilder.SetGasLimit(gasLimit) - - privs, accNums, accSeqs := []cryptotypes.PrivKey{c.msgSigner}, []uint64{3}, []uint64{0} - tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) - s.Require().NoError(err) - - // set zero gas prices - s.ctx = s.ctx.WithMinGasPrices(sdk.NewDecCoins()) - - feeCollectorBefore := bk.GetBalance(s.ctx, feeCollector.GetAddress(), core.MicroSDRDenom) - burnBefore := bk.GetBalance(s.ctx, burnModule.GetAddress(), core.MicroSDRDenom) - communityBefore := dk.GetFeePool(s.ctx).CommunityPool.AmountOf(core.MicroSDRDenom) - supplyBefore := bk.GetSupply(s.ctx, core.MicroSDRDenom) - - _, err = antehandler(s.ctx, tx, false) - if i == 0 && c.expectedFeeAmount != 0 { - s.Require().EqualError(err, fmt.Sprintf( - "insufficient fees; got: \"\", required: \"%dusdr\" = \"\"(gas) + \"%dusdr\"(stability): insufficient fee", - c.expectedFeeAmount, c.expectedFeeAmount)) - } else { - s.Require().NoError(err) - } - - feeCollectorAfter := bk.GetBalance(s.ctx, feeCollector.GetAddress(), core.MicroSDRDenom) - burnAfter := bk.GetBalance(s.ctx, burnModule.GetAddress(), core.MicroSDRDenom) - communityAfter := dk.GetFeePool(s.ctx).CommunityPool.AmountOf(core.MicroSDRDenom) - supplyAfter := bk.GetSupply(s.ctx, core.MicroSDRDenom) - - if i == 0 { - s.Require().Equal(feeCollectorBefore, feeCollectorAfter) - s.Require().Equal(burnBefore, burnAfter) - s.Require().Equal(communityBefore, communityAfter) - s.Require().Equal(supplyBefore, supplyAfter) - } - - if i == 1 { - s.Require().Equal(feeCollectorBefore, feeCollectorAfter) - splitAmount := burnSplitRate.MulInt64(c.expectedFeeAmount).TruncateInt() - s.Require().Equal(burnBefore, burnAfter.AddAmount(splitAmount)) - s.Require().Equal(communityBefore, communityAfter.Add(sdk.NewDecFromInt(splitAmount))) - s.Require().Equal(supplyBefore, supplyAfter.SubAmount(splitAmount)) - } - } - }) - } -} diff --git a/custom/auth/client/utils/feeutils.go b/custom/auth/client/utils/feeutils.go index cd45132ce..f99d5ee45 100644 --- a/custom/auth/client/utils/feeutils.go +++ b/custom/auth/client/utils/feeutils.go @@ -1,22 +1,12 @@ package utils import ( - "context" - - "cosmossdk.io/math" "github.com/spf13/pflag" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" - "github.com/cosmos/cosmos-sdk/x/authz" - - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - marketexported "github.com/classic-terra/core/v3/x/market/exported" - treasuryexported "github.com/classic-terra/core/v3/x/treasury/exported" ) type ( @@ -62,13 +52,9 @@ func ComputeFeesWithCmd( gas = adj } - // Computes taxes of the msgs - taxes, err := FilterMsgAndComputeTax(clientCtx, msgs...) - if err != nil { - return nil, err - } - - fees := txf.Fees().Add(taxes...) + // As the tax is already converted to gas when simulating, + // we don't need to calculate tax anymore + fees := txf.Fees() gasPrices := txf.GasPrices() if !gasPrices.IsZero() { @@ -96,129 +82,6 @@ func ComputeFeesWithCmd( }, nil } -// FilterMsgAndComputeTax computes the stability tax on MsgSend and MsgMultiSend. -func FilterMsgAndComputeTax(clientCtx client.Context, msgs ...sdk.Msg) (taxes sdk.Coins, err error) { - taxRate, err := queryTaxRate(clientCtx) - if err != nil { - return nil, err - } - - for _, msg := range msgs { - switch msg := msg.(type) { - case *banktypes.MsgSend: - tax, err := computeTax(clientCtx, taxRate, msg.Amount) - if err != nil { - return nil, err - } - - taxes = taxes.Add(tax...) - - case *banktypes.MsgMultiSend: - for _, input := range msg.Inputs { - tax, err := computeTax(clientCtx, taxRate, input.Coins) - if err != nil { - return nil, err - } - - taxes = taxes.Add(tax...) - } - - case *authz.MsgExec: - messages, err := msg.GetMessages() - if err != nil { - panic(err) - } - - tax, err := FilterMsgAndComputeTax(clientCtx, messages...) - if err != nil { - return nil, err - } - - taxes = taxes.Add(tax...) - - case *marketexported.MsgSwapSend: - tax, err := computeTax(clientCtx, taxRate, sdk.NewCoins(msg.OfferCoin)) - if err != nil { - return nil, err - } - - taxes = taxes.Add(tax...) - - case *wasmtypes.MsgInstantiateContract: - tax, err := computeTax(clientCtx, taxRate, msg.Funds) - if err != nil { - return nil, err - } - - taxes = taxes.Add(tax...) - - case *wasmtypes.MsgInstantiateContract2: - tax, err := computeTax(clientCtx, taxRate, msg.Funds) - if err != nil { - return nil, err - } - - taxes = taxes.Add(tax...) - - case *wasmtypes.MsgExecuteContract: - tax, err := computeTax(clientCtx, taxRate, msg.Funds) - if err != nil { - return nil, err - } - - taxes = taxes.Add(tax...) - } - } - - return taxes, nil -} - -// computes the stability tax according to tax-rate and tax-cap -func computeTax(clientCtx client.Context, taxRate sdk.Dec, principal sdk.Coins) (taxes sdk.Coins, err error) { - for _, coin := range principal { - - taxCap, err := queryTaxCap(clientCtx, coin.Denom) - if err != nil { - return nil, err - } - - taxDue := sdk.NewDecFromInt(coin.Amount).Mul(taxRate).TruncateInt() - - // If tax due is greater than the tax cap, cap! - if taxDue.GT(taxCap) { - taxDue = taxCap - } - - if taxDue.Equal(sdk.ZeroInt()) { - continue - } - - taxes = taxes.Add(sdk.NewCoin(coin.Denom, taxDue)) - } - - return -} - -func queryTaxRate(clientCtx client.Context) (sdk.Dec, error) { - queryClient := treasuryexported.NewQueryClient(clientCtx) - - res, err := queryClient.TaxRate(context.Background(), &treasuryexported.QueryTaxRateRequest{}) - if err != nil { - return sdk.ZeroDec(), err - } - return res.TaxRate, err -} - -func queryTaxCap(clientCtx client.Context, denom string) (math.Int, error) { - queryClient := treasuryexported.NewQueryClient(clientCtx) - - res, err := queryClient.TaxCap(context.Background(), &treasuryexported.QueryTaxCapRequest{Denom: denom}) - if err != nil { - return sdk.NewInt(0), err - } - return res.TaxCap, err -} - // prepareFactory ensures the account defined by ctx.GetFromAddress() exists and // if the account number and/or the account sequence number are zero (not set), // they will be queried for and set on the provided Factory. A new Factory with diff --git a/custom/auth/post/post.go b/custom/auth/post/post.go index 95f0f1867..5183346cc 100644 --- a/custom/auth/post/post.go +++ b/custom/auth/post/post.go @@ -3,12 +3,22 @@ package post import ( dyncommkeeper "github.com/classic-terra/core/v3/x/dyncomm/keeper" dyncommpost "github.com/classic-terra/core/v3/x/dyncomm/post" + tax2gaskeeper "github.com/classic-terra/core/v3/x/tax2gas/keeper" + tax2gaspost "github.com/classic-terra/core/v3/x/tax2gas/post" + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/ante" ) // HandlerOptions are the options required for constructing a default SDK AnteHandler. type HandlerOptions struct { - DyncommKeeper dyncommkeeper.Keeper + AccountKeeper ante.AccountKeeper + BankKeeper tax2gastypes.BankKeeper + FeegrantKeeper tax2gastypes.FeegrantKeeper + DyncommKeeper dyncommkeeper.Keeper + TreasuryKeeper tax2gastypes.TreasuryKeeper + DistrKeeper tax2gastypes.DistrKeeper + Tax2Gaskeeper tax2gaskeeper.Keeper } // NewPostHandler returns an PostHandler that checks and set target @@ -16,5 +26,6 @@ type HandlerOptions struct { func NewPostHandler(options HandlerOptions) (sdk.PostHandler, error) { return sdk.ChainPostDecorators( dyncommpost.NewDyncommPostDecorator(options.DyncommKeeper), + tax2gaspost.NewTax2GasPostDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TreasuryKeeper, options.DistrKeeper, options.Tax2Gaskeeper), ), nil } diff --git a/custom/auth/tx/service.go b/custom/auth/tx/service.go index 63710d04e..f227a8164 100644 --- a/custom/auth/tx/service.go +++ b/custom/auth/tx/service.go @@ -9,6 +9,7 @@ import ( "google.golang.org/grpc/status" customante "github.com/classic-terra/core/v3/custom/auth/ante" + tax2gasUtils "github.com/classic-terra/core/v3/x/tax2gas/utils" "github.com/cosmos/cosmos-sdk/client" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -21,13 +22,15 @@ var _ ServiceServer = txServer{} type txServer struct { clientCtx client.Context treasuryKeeper customante.TreasuryKeeper + tax2gasKeeper customante.Tax2GasKeeper } // NewTxServer creates a new Tx service server. -func NewTxServer(clientCtx client.Context, treasuryKeeper customante.TreasuryKeeper) ServiceServer { +func NewTxServer(clientCtx client.Context, treasuryKeeper customante.TreasuryKeeper, tax2gasKeeper customante.Tax2GasKeeper) ServiceServer { return txServer{ clientCtx: clientCtx, treasuryKeeper: treasuryKeeper, + tax2gasKeeper: tax2gasKeeper, } } @@ -52,7 +55,8 @@ func (ts txServer) ComputeTax(c context.Context, req *ComputeTaxRequest) (*Compu return nil, status.Errorf(codes.InvalidArgument, "empty txBytes is not allowed") } - taxAmount := customante.FilterMsgAndComputeTax(ctx, ts.treasuryKeeper, msgs...) + burnTaxRate := ts.tax2gasKeeper.GetBurnTaxRate(ctx) + taxAmount := tax2gasUtils.FilterMsgAndComputeTax(ctx, ts.treasuryKeeper, burnTaxRate, msgs...) return &ComputeTaxResponse{ TaxAmount: taxAmount, }, nil @@ -63,10 +67,11 @@ func RegisterTxService( qrt gogogrpc.Server, clientCtx client.Context, treasuryKeeper customante.TreasuryKeeper, + tax2gasKeeper customante.Tax2GasKeeper, ) { RegisterServiceServer( qrt, - NewTxServer(clientCtx, treasuryKeeper), + NewTxServer(clientCtx, treasuryKeeper, tax2gasKeeper), ) } diff --git a/custom/wasm/keeper/handler_plugin.go b/custom/wasm/keeper/handler_plugin.go index f3a24801b..bff98dae1 100644 --- a/custom/wasm/keeper/handler_plugin.go +++ b/custom/wasm/keeper/handler_plugin.go @@ -1,20 +1,22 @@ package keeper import ( - "github.com/classic-terra/core/v3/custom/auth/ante" - treasurykeeper "github.com/classic-terra/core/v3/x/treasury/keeper" - - wasmvmtypes "github.com/CosmWasm/wasmvm/types" + errorsmod "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/baseapp" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - cosmosante "github.com/cosmos/cosmos-sdk/x/auth/ante" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" bankKeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + wasmvmtypes "github.com/CosmWasm/wasmvm/types" + + tax2gaskeeper "github.com/classic-terra/core/v3/x/tax2gas/keeper" + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" + tax2gasutils "github.com/classic-terra/core/v3/x/tax2gas/utils" + treasurykeeper "github.com/classic-terra/core/v3/x/treasury/keeper" ) // msgEncoder is an extension point to customize encodings @@ -35,6 +37,7 @@ type SDKMessageHandler struct { treasuryKeeper treasurykeeper.Keeper accountKeeper authkeeper.AccountKeeper bankKeeper bankKeeper.Keeper + tax2gaskeeper tax2gaskeeper.Keeper } func NewMessageHandler( @@ -45,6 +48,7 @@ func NewMessageHandler( bankKeeper bankKeeper.Keeper, treasuryKeeper treasurykeeper.Keeper, accountKeeper authkeeper.AccountKeeper, + tax2gaskeeper tax2gaskeeper.Keeper, unpacker codectypes.AnyUnpacker, portSource wasmtypes.ICS20TransferPortSource, customEncoders ...*wasmkeeper.MessageEncoders, @@ -54,19 +58,20 @@ func NewMessageHandler( encoders = encoders.Merge(e) } return wasmkeeper.NewMessageHandlerChain( - NewSDKMessageHandler(router, encoders, treasuryKeeper, accountKeeper, bankKeeper), + NewSDKMessageHandler(router, encoders, treasuryKeeper, accountKeeper, bankKeeper, tax2gaskeeper), wasmkeeper.NewIBCRawPacketHandler(ics4Wrapper, channelKeeper, capabilityKeeper), wasmkeeper.NewBurnCoinMessageHandler(bankKeeper), ) } -func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder, treasuryKeeper treasurykeeper.Keeper, accountKeeper authkeeper.AccountKeeper, bankKeeper bankKeeper.Keeper) SDKMessageHandler { +func NewSDKMessageHandler(router MessageRouter, encoders msgEncoder, treasuryKeeper treasurykeeper.Keeper, accountKeeper authkeeper.AccountKeeper, bankKeeper bankKeeper.Keeper, tax2gaskeeper tax2gaskeeper.Keeper) SDKMessageHandler { return SDKMessageHandler{ router: router, encoders: encoders, treasuryKeeper: treasuryKeeper, accountKeeper: accountKeeper, bankKeeper: bankKeeper, + tax2gaskeeper: tax2gaskeeper, } } @@ -76,17 +81,25 @@ func (h SDKMessageHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddr return nil, nil, err } + gasPrices, ok := ctx.Value(tax2gastypes.FinalGasPrices).(sdk.DecCoins) + if !ok { + gasPrices = h.tax2gaskeeper.GetGasPrices(ctx) + } for _, sdkMsg := range sdkMsgs { - // Charge tax on result msg - taxes := ante.FilterMsgAndComputeTax(ctx, h.treasuryKeeper, sdkMsg) - if !taxes.IsZero() { - eventManager := sdk.NewEventManager() - contractAcc := h.accountKeeper.GetAccount(ctx, contractAddr) - if err := cosmosante.DeductFees(h.bankKeeper, ctx.WithEventManager(eventManager), contractAcc, taxes); err != nil { - return nil, nil, err - } + if h.tax2gaskeeper.IsEnabled(ctx) { + burnTaxRate := h.tax2gaskeeper.GetBurnTaxRate(ctx) + taxes := tax2gasutils.FilterMsgAndComputeTax(ctx, h.treasuryKeeper, burnTaxRate, sdkMsg) + if !taxes.IsZero() { + eventManager := sdk.NewEventManager() - events = eventManager.Events() + taxGas, err := tax2gasutils.ComputeGas(gasPrices, taxes) + if err != nil { + return nil, nil, err + } + ctx.TaxGasMeter().ConsumeGas(taxGas, "tax gas") + + events = eventManager.Events() + } } res, err := h.handleSdkMessage(ctx, contractAddr, sdkMsg) @@ -112,7 +125,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") } } @@ -127,5 +140,5 @@ 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) } diff --git a/go.mod b/go.mod index c0b02b491..194bfc475 100644 --- a/go.mod +++ b/go.mod @@ -229,7 +229,7 @@ replace ( // 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 - github.com/cosmos/cosmos-sdk => github.com/classic-terra/cosmos-sdk v0.47.10-terra.1 + github.com/cosmos/cosmos-sdk => github.com/classic-terra/cosmos-sdk v0.47.10-terra.1.0.20240731055430-cf7f52e8ee42 github.com/cosmos/ibc-go/v7 => github.com/classic-terra/ibc-go/v7 v7.4.0-terra github.com/cosmos/ledger-cosmos-go => github.com/terra-money/ledger-terra-go v0.11.2 // replace goleveldb to optimized one diff --git a/go.sum b/go.sum index bfc5d875b..8dcf4bd49 100644 --- a/go.sum +++ b/go.sum @@ -350,8 +350,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/classic-terra/cometbft v0.37.4-terra1 h1:eT5B2n5KKi5WVW+3ZNOVTmtfKKaZrXOLX9G80m9mhZo= github.com/classic-terra/cometbft v0.37.4-terra1/go.mod h1:vFqj7Qe3uFFJvHZleTJPQDmJ/WscXHi4rKWqiCAaNZk= -github.com/classic-terra/cosmos-sdk v0.47.10-terra.1 h1:ek0vQ435fpeP3xGhszDO2yMIRy5XGMj9MCTlvpMUIkw= -github.com/classic-terra/cosmos-sdk v0.47.10-terra.1/go.mod h1:4mBvTB8zevoeTuQufWwTcNnthGG2afXO+9D42BKzlRo= +github.com/classic-terra/cosmos-sdk v0.47.10-terra.1.0.20240731055430-cf7f52e8ee42 h1:Dr00n/hlWF4biEN/i2MwmNk3iOZ1e+PUraf72gNEqYU= +github.com/classic-terra/cosmos-sdk v0.47.10-terra.1.0.20240731055430-cf7f52e8ee42/go.mod h1:4mBvTB8zevoeTuQufWwTcNnthGG2afXO+9D42BKzlRo= github.com/classic-terra/goleveldb v0.0.0-20230914223247-2b28f6655121 h1:fjpWDB0hm225wYg9vunyDyTH8ftd5xEUgINJKidj+Tw= 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= diff --git a/proto/terra/tax2gas/v1beta1/genesis.proto b/proto/terra/tax2gas/v1beta1/genesis.proto new file mode 100644 index 000000000..799f6101b --- /dev/null +++ b/proto/terra/tax2gas/v1beta1/genesis.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; +package terra.tax2gas.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "amino/amino.proto"; + +option go_package = "github.com/classic-terra/core/v3/x/tax2gas/types"; + +message Params { + option (gogoproto.goproto_stringer) = true; + option (amino.name) = "terra/x/tax2gas/Params"; + + repeated cosmos.base.v1beta1.DecCoin gas_prices = 1 [ + (gogoproto.moretags) = "yaml:\"gas_prices\"", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins", + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true + ]; + + string burn_tax_rate = 2 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; + + bool enabled = 3; + + // bypass_min_fee_msg_types defines a list of message type urls + // that are free of fee charge. + repeated string bypass_min_fee_msg_types = 4 [ + (gogoproto.jsontag) = "bypass_min_fee_msg_types,omitempty", + (gogoproto.moretags) = "yaml:\"bypass_min_fee_msg_types\"" + ]; + + // max_total_bypass_min_fee_msg_gas_usage defines the total maximum gas usage + // allowed for a transaction containing only messages of types in bypass_min_fee_msg_types + // to bypass fee charge. + uint64 max_total_bypass_min_fee_msg_gas_usage = 5; +} + +// GenesisState defines the tax2gas module's genesis state. +message GenesisState { + // params is the container of tax2gas parameters. + Params params = 1 [(gogoproto.nullable) = false]; +} \ No newline at end of file diff --git a/proto/terra/tax2gas/v1beta1/query.proto b/proto/terra/tax2gas/v1beta1/query.proto new file mode 100644 index 000000000..bb4d635ab --- /dev/null +++ b/proto/terra/tax2gas/v1beta1/query.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +package terra.tax2gas.v1beta1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "terra/tax2gas/v1beta1/genesis.proto"; + +option go_package = "github.com/classic-terra/core/v3/x/tax2gas/types"; + +service Query { + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/terra/tax2gas/Params"; + } + rpc BurnTaxRate(QueryBurnTaxRateRequest) returns (QueryBurnTaxRateResponse) { + option (google.api.http).get = "/terra/tax2gas/BurnTaxRate"; + } +} + +//=============================== Params +message QueryParamsRequest {} +message QueryParamsResponse { + Params params = 1 [(gogoproto.nullable) = false]; +} + +message QueryBurnTaxRateRequest {} +message QueryBurnTaxRateResponse { + string burn_tax_rate = 1 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", + (gogoproto.nullable) = false + ]; +} \ No newline at end of file diff --git a/proto/terra/tax2gas/v1beta1/tx.proto b/proto/terra/tax2gas/v1beta1/tx.proto new file mode 100644 index 000000000..87d8f61c1 --- /dev/null +++ b/proto/terra/tax2gas/v1beta1/tx.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; +package terra.tax2gas.v1beta1; + +import "gogoproto/gogo.proto"; +import "cosmos/msg/v1/msg.proto"; +import "amino/amino.proto"; +import "cosmos_proto/cosmos.proto"; +import "terra/tax2gas/v1beta1/genesis.proto"; + +option go_package = "github.com/classic-terra/core/v3/x/tax2gas/types"; + +service Msg { + option (cosmos.msg.v1.service) = true; + + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + + // authority is the address that controls the module (defaults to x/gov unless overwritten). + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + option (amino.name) = "terra/x/tax2gas/MsgUpdateParams"; + + // params defines the x/tax2gas parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +// +// Since: cosmos-sdk 0.47 +message MsgUpdateParamsResponse {} \ No newline at end of file diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index bfcae1a32..674c4958a 100755 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -2,9 +2,6 @@ set -eo pipefail -# get protoc executions -go get github.com/regen-network/cosmos-proto/protoc-gen-gocosmos 2>/dev/null - echo "Generating gogo proto code" cd proto proto_dirs=$(find terra -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) diff --git a/tests/e2e/configurer/chain/commands.go b/tests/e2e/configurer/chain/commands.go index c1e96bac5..f419217a5 100644 --- a/tests/e2e/configurer/chain/commands.go +++ b/tests/e2e/configurer/chain/commands.go @@ -25,18 +25,25 @@ import ( func (n *NodeConfig) StoreWasmCode(wasmFile, from string) { n.LogActionF("storing wasm code from file %s", wasmFile) - cmd := []string{"terrad", "tx", "wasm", "store", wasmFile, fmt.Sprintf("--from=%s", from)} + cmd := []string{"terrad", "tx", "wasm", "store", wasmFile, fmt.Sprintf("--from=%s", from), "--fees=10uluna", "--gas=2000000"} _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully stored") } -func (n *NodeConfig) InstantiateWasmContract(codeID, initMsg, amount, from string) { +func (n *NodeConfig) InstantiateWasmContract(codeID, initMsg, amount, from string, feeDenoms []string, fees ...sdk.Coin) { n.LogActionF("instantiating wasm contract %s with %s", codeID, initMsg) cmd := []string{"terrad", "tx", "wasm", "instantiate", codeID, initMsg, fmt.Sprintf("--from=%s", from), "--no-admin", "--label=ratelimit"} if amount != "" { cmd = append(cmd, fmt.Sprintf("--amount=%s", amount)) } + if len(fees) == 0 { + gasPrices := feeDenomsToGasPrices(feeDenoms) + cmd = append(cmd, "--gas", "auto", "--gas-adjustment=1.2", fmt.Sprintf("--gas-prices=%s", gasPrices)) + } else { + feeCoins := sdk.NewCoins(fees...) + cmd = append(cmd, "--fees", feeCoins.String(), "--gas", "auto", "--gas-adjustment=1.2") + } n.LogActionF(strings.Join(cmd, " ")) _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) @@ -45,7 +52,7 @@ func (n *NodeConfig) InstantiateWasmContract(codeID, initMsg, amount, from strin n.LogActionF("successfully initialized") } -func (n *NodeConfig) Instantiate2WasmContract(codeID, initMsg, salt, amount, fee, gas, from string) { +func (n *NodeConfig) Instantiate2WasmContract(codeID, initMsg, salt, amount, from string, feeDenoms []string) { n.LogActionF("instantiating wasm contract %s with %s", codeID, initMsg) encodedSalt := make([]byte, hex.EncodedLen(len([]byte(salt)))) hex.Encode(encodedSalt, []byte(salt)) @@ -53,33 +60,45 @@ func (n *NodeConfig) Instantiate2WasmContract(codeID, initMsg, salt, amount, fee if amount != "" { cmd = append(cmd, fmt.Sprintf("--amount=%s", amount)) } - if fee != "" { - cmd = append(cmd, fmt.Sprintf("--fees=%s", fee)) - } - if gas != "" { - cmd = append(cmd, fmt.Sprintf("--gas=%s", gas)) - } + gasPrices := feeDenomsToGasPrices(feeDenoms) + cmd = append(cmd, "--gas", "auto", "--gas-adjustment=1.2", fmt.Sprintf("--gas-prices=%s", gasPrices)) + n.LogActionF(strings.Join(cmd, " ")) _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully initialized") } -func (n *NodeConfig) WasmExecute(contract, execMsg, amount, fee, from string) { +func (n *NodeConfig) WasmExecute(contract, execMsg, amount, from string, feeDenoms []string) { n.LogActionF("executing %s on wasm contract %s from %s", execMsg, contract, from) cmd := []string{"terrad", "tx", "wasm", "execute", contract, execMsg, fmt.Sprintf("--from=%s", from)} if amount != "" { cmd = append(cmd, fmt.Sprintf("--amount=%s", amount)) } - if fee != "" { - cmd = append(cmd, fmt.Sprintf("--fees=%s", fee)) - } + gasPrices := feeDenomsToGasPrices(feeDenoms) + cmd = append(cmd, "--gas", "auto", "--gas-adjustment=1.2", fmt.Sprintf("--gas-prices=%s", gasPrices)) + n.LogActionF(strings.Join(cmd, " ")) _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully executed") } +func (n *NodeConfig) WasmExecuteError(contract, execMsg, amount, from string, feeDenoms []string) { + n.LogActionF("executing %s on wasm contract %s from %s", execMsg, contract, from) + cmd := []string{"terrad", "tx", "wasm", "execute", contract, execMsg, fmt.Sprintf("--from=%s", from)} + if amount != "" { + cmd = append(cmd, fmt.Sprintf("--amount=%s", amount)) + } + gasPrices := feeDenomsToGasPrices(feeDenoms) + cmd = append(cmd, "--gas", "auto", "--gas-adjustment=1.2", fmt.Sprintf("--gas-prices=%s", gasPrices)) + + n.LogActionF(strings.Join(cmd, " ")) + _, _, err := n.containerManager.ExecTxCmdError(n.t, n.chainID, n.Name, cmd, "", true) + require.NoError(n.t, err) + n.LogActionF("executed failed") +} + // QueryParams extracts the params for a given subspace and key. This is done generically via json to avoid having to // specify the QueryParamResponse type (which may not exist for all params). func (n *NodeConfig) QueryParams(subspace, key string, result any) { @@ -124,13 +143,13 @@ func (n *NodeConfig) SubmitAddBurnTaxExemptionAddressProposal(addresses []string "add-burn-tax-exemption-address", strings.Join(addresses, ","), "--title=\"burn tax exemption address\"", "--description=\"\"burn tax exemption address", + "--gas", "300000", "--gas-prices", "1uluna", fmt.Sprintf("--from=%s", walletName), } resp, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) - fmt.Println("resp: ", resp.String()) proposalID, err := extractProposalIDFromResponse(resp.String()) require.NoError(n.t, err) @@ -152,7 +171,7 @@ func (n *NodeConfig) FailIBCTransfer(from, recipient, amount string) { func (n *NodeConfig) SendIBCTransfer(from, recipient, amount, memo string) { n.LogActionF("IBC sending %s from %s to %s. memo: %s", amount, from, recipient, memo) - cmd := []string{"terrad", "tx", "ibc-transfer", "transfer", "transfer", "channel-0", recipient, amount, fmt.Sprintf("--from=%s", from), "--memo", memo} + cmd := []string{"terrad", "tx", "ibc-transfer", "transfer", "transfer", "channel-0", recipient, amount, fmt.Sprintf("--from=%s", from), "--memo", memo, "--fees=10uluna"} _, _, err := n.containerManager.ExecTxCmdWithSuccessString(n.t, n.chainID, n.Name, cmd, "\"code\":0") require.NoError(n.t, err) @@ -171,7 +190,7 @@ func (n *NodeConfig) SubmitTextProposal(text string, initialDeposit sdk.Coin) { func (n *NodeConfig) DepositProposal(proposalNumber int) { n.LogActionF("depositing on proposal: %d", proposalNumber) deposit := sdk.NewCoin(initialization.TerraDenom, sdk.NewInt(20*assets.MicroUnit)).String() - cmd := []string{"terrad", "tx", "gov", "deposit", fmt.Sprintf("%d", proposalNumber), deposit, "--from=val"} + cmd := []string{"terrad", "tx", "gov", "deposit", fmt.Sprintf("%d", proposalNumber), deposit, "--from=val", "--gas", "300000", "--fees", "10000000uluna"} _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully deposited on proposal %d", proposalNumber) @@ -179,7 +198,7 @@ func (n *NodeConfig) DepositProposal(proposalNumber int) { func (n *NodeConfig) VoteYesProposal(from string, proposalNumber int) { n.LogActionF("voting yes on proposal: %d", proposalNumber) - cmd := []string{"terrad", "tx", "gov", "vote", fmt.Sprintf("%d", proposalNumber), "yes", fmt.Sprintf("--from=%s", from)} + cmd := []string{"terrad", "tx", "gov", "vote", fmt.Sprintf("%d", proposalNumber), "yes", fmt.Sprintf("--from=%s", from), "--gas", "300000", "--fees", "10000000uluna"} _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully voted yes on proposal %d", proposalNumber) @@ -216,44 +235,77 @@ func extractProposalIDFromResponse(response string) (int, error) { return proposalID, nil } -func (n *NodeConfig) BankSend(amount string, sendAddress string, receiveAddress string) { - n.BankSendWithWallet(amount, sendAddress, receiveAddress, "val") +func (n *NodeConfig) BankSendError(amount string, sendAddress string, receiveAddress string, walletName string, gasLimit string, fees sdk.Coins, failError string) { + n.LogActionF("bank sending %s from address %s to %s", amount, sendAddress, receiveAddress) + cmd := []string{"terrad", "tx", "bank", "send", sendAddress, receiveAddress, amount, fmt.Sprintf("--from=%s", walletName)} + cmd = append(cmd, "--fees", fees.String(), "--gas", gasLimit) + _, _, err := n.containerManager.ExecTxCmdError(n.t, n.chainID, n.Name, cmd, failError, false) + require.NoError(n.t, err) + n.LogActionF("failed sent bank sent %s from address %s to %s", amount, sendAddress, receiveAddress) +} + +func (n *NodeConfig) BankSend(amount string, sendAddress string, receiveAddress string, feeDenoms []string, fees ...sdk.Coin) { + n.BankSendWithWallet(amount, sendAddress, receiveAddress, "val", feeDenoms, fees...) } -func (n *NodeConfig) BankSendWithWallet(amount string, sendAddress string, receiveAddress string, walletName string) { +func (n *NodeConfig) BankSendWithWallet(amount string, sendAddress string, receiveAddress string, walletName string, feeDenoms []string, fees ...sdk.Coin) { n.LogActionF("bank sending %s from address %s to %s", amount, sendAddress, receiveAddress) cmd := []string{"terrad", "tx", "bank", "send", sendAddress, receiveAddress, amount, fmt.Sprintf("--from=%s", walletName)} + gasPrices := feeDenomsToGasPrices(feeDenoms) + if len(fees) == 0 { + cmd = append(cmd, "--gas", "auto", "--gas-adjustment=1.2", fmt.Sprintf("--gas-prices=%s", gasPrices)) + } else { + feeCoins := sdk.NewCoins(fees...) + cmd = append(cmd, "--fees", feeCoins.String(), "--gas", "auto", "--gas-adjustment=1.2") + } _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully sent bank sent %s from address %s to %s", amount, sendAddress, receiveAddress) } -func (n *NodeConfig) BankSendFeeGrantWithWallet(amount string, sendAddress string, receiveAddress string, feeGranter string, walletName string) { +func (n *NodeConfig) BankSendFeeGrantWithWallet(amount string, sendAddress string, receiveAddress string, feeGranter string, walletName string, feeDenoms []string, fees ...sdk.Coin) { n.LogActionF("bank sending %s from address %s to %s", amount, sendAddress, receiveAddress) cmd := []string{"terrad", "tx", "bank", "send", sendAddress, receiveAddress, amount, fmt.Sprintf("--fee-granter=%s", feeGranter), fmt.Sprintf("--from=%s", walletName)} + gasPrices := feeDenomsToGasPrices(feeDenoms) + if len(fees) == 0 { + cmd = append(cmd, "--gas", "auto", "--gas-adjustment=1.2", fmt.Sprintf("--gas-prices=%s", gasPrices)) + } else { + feeCoins := sdk.NewCoins(fees...) + cmd = append(cmd, "--fees", feeCoins.String(), "--gas", "auto", "--gas-adjustment=1.2") + } _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully sent bank sent %s from address %s to %s", amount, sendAddress, receiveAddress) } -func (n *NodeConfig) BankMultiSend(amount string, split bool, sendAddress string, receiveAddresses ...string) { +func (n *NodeConfig) BankMultiSend(amount string, split bool, sendAddress string, feeDenoms []string, receiveAddresses []string) { n.LogActionF("bank multisending from %s to %s", sendAddress, strings.Join(receiveAddresses, ",")) cmd := []string{"terrad", "tx", "bank", "multi-send", sendAddress} cmd = append(cmd, receiveAddresses...) cmd = append(cmd, amount, "--from=val") + + gasPrices := feeDenomsToGasPrices(feeDenoms) + cmd = append(cmd, "--gas", "auto", "--gas-adjustment=1.2", fmt.Sprintf("--gas-prices=%s", gasPrices)) if split { cmd = append(cmd, "--split") } - _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully multisent %s to %s", sendAddress, strings.Join(receiveAddresses, ",")) } -func (n *NodeConfig) GrantAddress(granter, gratee string, spendLimit string, walletName string) { +func (n *NodeConfig) GrantAddress(granter, gratee string, walletName string, feeDenom string, spendLimit ...string) { n.LogActionF("granting for address %s", gratee) - cmd := []string{"terrad", "tx", "feegrant", "grant", granter, gratee, fmt.Sprintf("--from=%s", walletName), fmt.Sprintf("--spend-limit=%s", spendLimit)} + cmd := []string{"terrad", "tx", "feegrant", "grant", granter, gratee, fmt.Sprintf("--from=%s", walletName), fmt.Sprintf("--spend-limit=%s", strings.Join(spendLimit, ",")), fmt.Sprintf("--fees=10%s", feeDenom)} + _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) + require.NoError(n.t, err) + n.LogActionF("successfully granted for address %s", gratee) +} + +func (n *NodeConfig) RevokeGrant(granter, gratee string, walletName string, feeDenom string) { + n.LogActionF("revoking grant for address %s", gratee) + cmd := []string{"terrad", "tx", "feegrant", "revoke", granter, gratee, fmt.Sprintf("--from=%s", walletName), fmt.Sprintf("--fees=10%s", feeDenom)} _, _, err := n.containerManager.ExecTxCmd(n.t, n.chainID, n.Name, cmd) require.NoError(n.t, err) n.LogActionF("successfully granted for address %s", gratee) @@ -315,3 +367,22 @@ func (n *NodeConfig) Status() (resultStatus, error) { //nolint } return result, nil } + +func feeDenomsToGasPrices(feeDenoms []string) string { + gasPrices := "" + for i, feeDenom := range feeDenoms { + switch feeDenom { + case initialization.TerraDenom: + gasPrices += fmt.Sprintf("%s%s", initialization.TerraGasPrice, initialization.TerraDenom) + case initialization.UsdDenom: + gasPrices += fmt.Sprintf("%s%s", initialization.UsdGasPrice, initialization.UsdDenom) + case initialization.EurDenom: + gasPrices += fmt.Sprintf("%s%s", initialization.EurGasPrice, initialization.EurDenom) + default: + } + if i != len(feeDenoms)-1 { + gasPrices += "," + } + } + return gasPrices +} diff --git a/tests/e2e/containers/containers.go b/tests/e2e/containers/containers.go index 8ffbbbd80..6cd0e854c 100644 --- a/tests/e2e/containers/containers.go +++ b/tests/e2e/containers/containers.go @@ -83,6 +83,23 @@ func (m *Manager) ExecTxCmd(t *testing.T, chainID string, containerName string, return m.ExecTxCmdWithSuccessString(t, chainID, containerName, command, "\"code\":0") } +// ExecTxCmdError Runs ExecCmd +func (m *Manager) ExecTxCmdError(t *testing.T, chainID string, containerName string, command []string, failStr string, checkTxHash bool) (bytes.Buffer, bytes.Buffer, error) { + allTxArgs := []string{fmt.Sprintf("--chain-id=%s", chainID), "--yes", "--keyring-backend=test", "--log_format=json"} + // parse to see if command has gas flags. If not, add default gas flags. + addGasFlags := true + for _, cmd := range command { + if strings.HasPrefix(cmd, "--gas") || strings.HasPrefix(cmd, "--fees") { + addGasFlags = false + } + } + if addGasFlags { + allTxArgs = append(allTxArgs, txDefaultGasArgs...) + } + txCommand := append(command, allTxArgs...) //nolint + return m.ExecCmd(t, containerName, txCommand, failStr, checkTxHash) +} + // ExecTxCmdWithSuccessString Runs ExecCmd, with flags for txs added. // namely adding flags `--chain-id={chain-id} --yes --keyring-backend=test "--log_format=json"`, // and searching for `successStr` @@ -104,6 +121,7 @@ func (m *Manager) ExecTxCmdWithSuccessString(t *testing.T, chainID string, conta // ExecHermesCmd executes command on the hermes relayer 1 container. func (m *Manager) ExecHermesCmd(t *testing.T, command []string, success string) (bytes.Buffer, bytes.Buffer, error) { + time.Sleep(time.Second * 30) return m.ExecCmd(t, hermesContainerName, command, success, false) } diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 26872dced..3a619954d 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -28,7 +28,7 @@ func (s *IntegrationTestSuite) TestIBCWasmHooks() { nodeA.InstantiateWasmContract( strconv.Itoa(chainA.LatestCodeID), `{"count": "0"}`, "", - initialization.ValidatorWalletName) + initialization.ValidatorWalletName, []string{}, sdk.NewCoin(initialization.TerraDenom, sdk.NewInt(2))) contracts, err := nodeA.QueryContractsFromID(chainA.LatestCodeID) s.NoError(err) @@ -104,6 +104,7 @@ func (s *IntegrationTestSuite) TestAddBurnTaxExemptionAddress() { s.Require().Contains(whitelistedAddresses, whitelistAddr2) } +// Each tx gas will cost 2 uluna (1 is for ante handler, 1 is for post handler) func (s *IntegrationTestSuite) TestFeeTax() { chain := s.configurer.GetChainConfig(0) node, err := chain.GetDefaultNode() @@ -119,14 +120,16 @@ func (s *IntegrationTestSuite) TestFeeTax() { s.Require().NoError(err) test1Addr := node.CreateWallet("test1") + test2Addr := node.CreateWallet("test2") // Test 1: banktypes.MsgSend // burn tax with bank send - node.BankSend(transferCoin1.String(), validatorAddr, test1Addr) - subAmount := transferAmount1.Add(initialization.TaxRate.MulInt(transferAmount1).TruncateInt()) - decremented := validatorBalance.Sub(sdk.NewCoin(initialization.TerraDenom, subAmount)) + node.BankSend(transferCoin1.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + + // Due to the fee estimate when using + decremented := validatorBalance.Sub(sdk.NewCoin(initialization.TerraDenom, subAmount.AddRaw(2))) newValidatorBalance, err := node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) s.Require().NoError(err) @@ -136,62 +139,38 @@ func (s *IntegrationTestSuite) TestFeeTax() { s.Require().Equal(balanceTest1.Amount, transferAmount1) s.Require().Equal(newValidatorBalance, decremented) - // Test 2: try bank send with grant - test2Addr := node.CreateWallet("test2") - transferAmount2 := sdkmath.NewInt(10000000) - transferCoin2 := sdk.NewCoin(initialization.TerraDenom, transferAmount2) - - node.BankSend(transferCoin2.String(), validatorAddr, test2Addr) - node.GrantAddress(test2Addr, test1Addr, transferCoin2.String(), "test2") - + // Test 2: banktypes.MsgMultiSend validatorBalance, err = node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) s.Require().NoError(err) - node.BankSendFeeGrantWithWallet(transferCoin2.String(), test1Addr, validatorAddr, test2Addr, "test1") - - newValidatorBalance, err = node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) - s.Require().NoError(err) - - balanceTest1, err = node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) - s.Require().NoError(err) - - balanceTest2, err := node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) - s.Require().NoError(err) - - s.Require().Equal(balanceTest1.Amount, transferAmount1.Sub(transferAmount2)) - s.Require().Equal(newValidatorBalance, validatorBalance.Add(transferCoin2)) - s.Require().Equal(balanceTest2.Amount, transferAmount2.Sub(initialization.TaxRate.MulInt(transferAmount2).TruncateInt())) - - // Test 3: banktypes.MsgMultiSend - validatorBalance, err = node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) - s.Require().NoError(err) - - node.BankMultiSend(transferCoin1.String(), false, validatorAddr, test1Addr, test2Addr) + totalTransferAmount := transferAmount1.Mul(sdk.NewInt(2)) + node.BankMultiSend(transferCoin1.String(), false, validatorAddr, []string{initialization.TerraDenom}, []string{test1Addr, test2Addr}) newValidatorBalance, err = node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) s.Require().NoError(err) - totalTransferAmount := transferAmount1.Mul(sdk.NewInt(2)) subAmount = totalTransferAmount.Add(initialization.TaxRate.MulInt(totalTransferAmount).TruncateInt()) - s.Require().Equal(newValidatorBalance, validatorBalance.Sub(sdk.NewCoin(initialization.TerraDenom, subAmount))) + s.Require().Equal(newValidatorBalance, validatorBalance.Sub(sdk.NewCoin(initialization.TerraDenom, subAmount.AddRaw(2)))) } +// Each tx gas will cost 2 uluna (1 is for ante handler, 1 is for post handler) func (s *IntegrationTestSuite) TestFeeTaxWasm() { chain := s.configurer.GetChainConfig(0) node, err := chain.GetDefaultNode() s.Require().NoError(err) - testAddr := node.CreateWallet("test") + testAddr := node.CreateWallet("test-wasm") transferAmount := sdkmath.NewInt(100000000) transferCoin := sdk.NewCoin(initialization.TerraDenom, transferAmount) - node.BankSend(fmt.Sprintf("%suluna", transferAmount.Mul(sdk.NewInt(4))), initialization.ValidatorWalletName, testAddr) + + node.BankSend(fmt.Sprintf("%suluna", transferAmount.Mul(sdk.NewInt(4))), initialization.ValidatorWalletName, testAddr, []string{initialization.TerraDenom}) node.StoreWasmCode("counter.wasm", initialization.ValidatorWalletName) chain.LatestCodeID = int(node.QueryLatestWasmCodeID()) // instantiate contract and transfer 100000000uluna node.InstantiateWasmContract( strconv.Itoa(chain.LatestCodeID), `{"count": "0"}`, transferCoin.String(), - "test") + "test-wasm", []string{initialization.TerraDenom}) contracts, err := node.QueryContractsFromID(chain.LatestCodeID) s.Require().NoError(err) @@ -199,17 +178,15 @@ func (s *IntegrationTestSuite) TestFeeTaxWasm() { balance1, err := node.QuerySpecificBalance(testAddr, initialization.TerraDenom) s.Require().NoError(err) - // 400000000 - 100000000 - 100000000 * TaxRate = 300000000 - 10000000 * TaxRate + // 400000000 - 100000000 - 100000000 * TaxRate - 2 (gas) = 300000000 - 10000000 * TaxRate - 2 (gas) taxAmount := initialization.TaxRate.MulInt(transferAmount).TruncateInt() - s.Require().Equal(balance1.Amount, transferAmount.Mul(sdk.NewInt(3)).Sub(taxAmount)) - - stabilityFee := sdk.NewDecWithPrec(2, 2).MulInt(transferAmount) + s.Require().Equal(balance1.Amount, transferAmount.Mul(sdk.NewInt(3)).Sub(taxAmount).SubRaw(2)) node.Instantiate2WasmContract( strconv.Itoa(chain.LatestCodeID), `{"count": "0"}`, "salt", transferCoin.String(), - fmt.Sprintf("%duluna", stabilityFee), "300000", "test") + "test-wasm", []string{initialization.TerraDenom}) contracts, err = node.QueryContractsFromID(chain.LatestCodeID) s.Require().NoError(err) @@ -217,16 +194,366 @@ func (s *IntegrationTestSuite) TestFeeTaxWasm() { balance2, err := node.QuerySpecificBalance(testAddr, initialization.TerraDenom) s.Require().NoError(err) - // balance1 - 100000000 - 100000000 * TaxRate + // balance1 - 100000000 - 100000000 * TaxRate - 2 (gas) taxAmount = initialization.TaxRate.MulInt(transferAmount).TruncateInt() - s.Require().Equal(balance2.Amount, balance1.Amount.Sub(transferAmount).Sub(taxAmount)) + s.Require().Equal(balance2.Amount, balance1.Amount.Sub(transferAmount).Sub(taxAmount).SubRaw(2)) contractAddr := contracts[0] - node.WasmExecute(contractAddr, `{"donate": {}}`, transferCoin.String(), fmt.Sprintf("%duluna", stabilityFee), "test") + node.WasmExecute(contractAddr, `{"donate": {}}`, transferCoin.String(), "test-wasm", []string{initialization.TerraDenom}) balance3, err := node.QuerySpecificBalance(testAddr, initialization.TerraDenom) s.Require().NoError(err) - // balance2 - 100000000 - 100000000 * TaxRate + // balance2 - 100000000 - 100000000 * TaxRate - 2 (gas) taxAmount = initialization.TaxRate.MulInt(transferAmount).TruncateInt() - s.Require().Equal(balance3.Amount, balance2.Amount.Sub(transferAmount).Sub(taxAmount)) + s.Require().Equal(balance3.Amount, balance2.Amount.Sub(transferAmount).Sub(taxAmount).SubRaw(2)) +} + +// Each tx gas will cost 2 token (1 is for ante handler, 1 is for post handler) +func (s *IntegrationTestSuite) TestFeeTaxGrant() { + chain := s.configurer.GetChainConfig(0) + node, err := chain.GetDefaultNode() + s.Require().NoError(err) + + transferAmount1 := sdkmath.NewInt(100000000) + transferCoin1 := sdk.NewCoin(initialization.TerraDenom, transferAmount1) + + validatorAddr := node.GetWallet(initialization.ValidatorWalletName) + s.Require().NotEqual(validatorAddr, "") + + test1Addr := node.CreateWallet("test1-grant") + test2Addr := node.CreateWallet("test2-grant") + + // Test 1: try bank send with grant + node.BankSend(transferCoin1.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + node.BankSend(transferCoin1.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + node.BankSend(transferCoin1.String(), validatorAddr, test2Addr, []string{initialization.TerraDenom}) + node.GrantAddress(test2Addr, test1Addr, "test2-grant", initialization.TerraDenom, transferCoin1.String()) + + validatorBalance, err := node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) + s.Require().NoError(err) + + node.BankSendFeeGrantWithWallet(transferCoin1.String(), test1Addr, validatorAddr, test2Addr, "test1-grant", []string{initialization.TerraDenom}) + + newValidatorBalance, err := node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) + s.Require().NoError(err) + + balanceTest1, err := node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) + s.Require().NoError(err) + + balanceTest2, err := node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) + s.Require().NoError(err) + + s.Require().Equal(balanceTest1, transferCoin1) + s.Require().Equal(newValidatorBalance, validatorBalance.Add(transferCoin1)) + // addr2 lost 2uluna to pay for grant msg's gas, 100000000 * TaxRate + 2uluna to pay for bank send msg's tx fees, + s.Require().Equal(balanceTest2.Amount, transferAmount1.Sub(initialization.TaxRate.MulInt(transferAmount1).TruncateInt()).SubRaw(12)) + + // Test 3: try bank send with no grant + transferAmount2 := sdkmath.NewInt(200000000) + transferUsdCoin2 := sdk.NewCoin(initialization.UsdDenom, transferAmount2) + transferTerraCoin2 := sdk.NewCoin(initialization.TerraDenom, transferAmount2) + + node.BankSend(transferTerraCoin2.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + node.BankSend(transferUsdCoin2.String(), validatorAddr, test1Addr, []string{initialization.UsdDenom}) + node.BankSend(transferUsdCoin2.String(), validatorAddr, test2Addr, []string{initialization.UsdDenom}) + + // Revoke previous grant and grant new ones + node.RevokeGrant(test2Addr, test1Addr, "test2-grant", initialization.UsdDenom) + feeAmountTerraDenom := sdkmath.NewInt(10) + feeCoinTerraDenom := sdk.NewCoin(initialization.TerraDenom, feeAmountTerraDenom) + node.GrantAddress(test2Addr, test1Addr, "test2-grant", initialization.UsdDenom, sdk.NewCoins(transferUsdCoin2, feeCoinTerraDenom).String()) + + validatorTerraBalance, err := node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) + s.Require().NoError(err) + balanceTest2TerraBalance, err := node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) + s.Require().NoError(err) + + node.BankSendFeeGrantWithWallet(transferTerraCoin2.String(), test1Addr, validatorAddr, test2Addr, "test1-grant", []string{}, transferUsdCoin2, feeCoinTerraDenom) + + newValidatorTerraBalance, err := node.QuerySpecificBalance(validatorAddr, initialization.TerraDenom) + s.Require().NoError(err) + balanceTest1TerraBalance, err := node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) + s.Require().NoError(err) + balanceTest1UsdBalance, err := node.QuerySpecificBalance(test1Addr, initialization.UsdDenom) + s.Require().NoError(err) + newBalanceTest2TerraBalance, err := node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) + s.Require().NoError(err) + balanceTest2UsdBalance, err := node.QuerySpecificBalance(test2Addr, initialization.UsdDenom) + s.Require().NoError(err) + // The fee grant msg only support to pay by one denom, so only uusd balance will change + s.Require().Equal(newValidatorTerraBalance, validatorTerraBalance.Add(transferTerraCoin2)) + s.Require().Equal(balanceTest1TerraBalance, balanceTest1) + s.Require().Equal(balanceTest1UsdBalance, transferUsdCoin2) + // 1uluna will be used for ante handler + s.Require().Equal(newBalanceTest2TerraBalance.Amount, balanceTest2TerraBalance.Amount.SubRaw(1)) + s.Require().Equal( + balanceTest2UsdBalance.Amount, + transferAmount2.Sub( + initialization.TaxRate.MulInt(transferAmount2). // tax amount in the form of terra denom + Mul(initialization.UsdGasPrice.Quo(initialization.TerraGasPrice)). // convert terra denom to usd denom base on gas price + TruncateInt(), + ).SubRaw(21), // addr2 lost 10uusd to pay for revoke msg's gas, 10uusd to pay for grant msg's gas, 1uusd to pay for band send msg's gas + ) +} + +func (s *IntegrationTestSuite) TestFeeTaxNotSupport() { + if s.skipIBC { + s.T().Skip("Skipping IBC tests") + } + chainA := s.configurer.GetChainConfig(0) + chainB := s.configurer.GetChainConfig(1) + + nodeA, err := chainA.GetDefaultNode() + s.NoError(err) + nodeB, err := chainB.GetDefaultNode() + s.NoError(err) + + transferAmount1 := sdkmath.NewInt(30000000) + transferCoin1 := sdk.NewCoin(initialization.TerraDenom, transferAmount1) + + validatorAddrChainA := nodeA.GetWallet(initialization.ValidatorWalletName) + s.Require().NotEqual(validatorAddrChainA, "") + validatorAddrChainB := nodeB.GetWallet(initialization.ValidatorWalletName) + s.Require().NotEqual(validatorAddrChainB, "") + + testAddrChainA := nodeA.CreateWallet("test1-feetax-not-support") + test1AddrChainB := nodeB.CreateWallet("test1-feetax-not-support") + test2AddrChainB := nodeB.CreateWallet("test2-feetax-not-support") + + // Test 1: try bank send with ibc denom + nodeA.BankSend(transferCoin1.String(), validatorAddrChainA, testAddrChainA, []string{initialization.TerraDenom}) + nodeB.BankSend(transferCoin1.String(), validatorAddrChainB, test1AddrChainB, []string{initialization.TerraDenom}) + + transferAmount2 := sdkmath.NewInt(20000000) + transferCoin2 := sdk.NewCoin(initialization.TerraDenom, transferAmount2) + nodeA.SendIBCTransfer("test1-feetax-not-support", test1AddrChainB, transferCoin2.String(), "") + + // check the balance of the contract + s.Eventually( + func() bool { + balance, err := nodeB.QueryBalances(test1AddrChainB) + s.Require().NoError(err) + if len(balance) == 0 { + return false + } + return balance[0].Amount.Equal(transferAmount2) + }, + initialization.OneMin, + 10*time.Millisecond, + ) + terraIBCBalance, err := nodeB.QuerySpecificBalance(test1AddrChainB, initialization.TerraIBCDenom) + s.Require().NoError(err) + s.Require().Equal(terraIBCBalance.Amount, transferAmount2) + + terraBalance, err := nodeB.QuerySpecificBalance(test1AddrChainB, initialization.TerraDenom) + s.Require().NoError(err) + s.Require().Equal(terraBalance.Amount, transferAmount1) + + transferAmount3 := sdkmath.NewInt(10000000) + transferCoin3 := sdk.NewCoin(initialization.TerraIBCDenom, transferAmount3) + + nodeB.BankSend(transferCoin3.String(), test1AddrChainB, test2AddrChainB, []string{}, sdk.NewCoin(initialization.TerraDenom, sdkmath.NewInt(2))) + + newTerraIBCBalance, err := nodeB.QuerySpecificBalance(test1AddrChainB, initialization.TerraIBCDenom) + s.Require().NoError(err) + s.Require().Equal(newTerraIBCBalance.Amount, terraIBCBalance.Amount.Sub(transferAmount3)) + newTerraIBCBalance, err = nodeB.QuerySpecificBalance(test2AddrChainB, initialization.TerraIBCDenom) + s.Require().NoError(err) + s.Require().Equal(newTerraIBCBalance.Amount, transferAmount3) + + newTerraBalance, err := nodeB.QuerySpecificBalance(test1AddrChainB, initialization.TerraDenom) + s.Require().NoError(err) + // Tx will only cost 10uluna on chain B as gas + s.Require().Equal(newTerraBalance.Amount, terraBalance.Amount.Sub(sdkmath.NewInt(2))) +} + +func (s *IntegrationTestSuite) TestFeeTaxMultipleDenoms() { + chain := s.configurer.GetChainConfig(0) + node, err := chain.GetDefaultNode() + s.Require().NoError(err) + + transferAmount := sdkmath.NewInt(100000000) + transferCoin1 := sdk.NewCoin(initialization.TerraDenom, transferAmount) + transferCoin2 := sdk.NewCoin(initialization.UsdDenom, transferAmount) + + test1Addr := node.CreateWallet("test1-multiple-fees") + test2Addr := node.CreateWallet("test2-multiple-fees") + + validatorAddr := node.GetWallet(initialization.ValidatorWalletName) + s.Require().NotEqual(validatorAddr, "") + + node.BankSend(transferCoin1.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + node.BankSend(transferCoin1.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + + node.BankSend(transferCoin2.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + + taxByTerraDenom := initialization.TaxRate.MulInt(transferAmount.QuoRaw(2)).TruncateInt() + feeByTerraDenom := sdk.NewCoin(initialization.TerraDenom, taxByTerraDenom.AddRaw(1)) // 1 uluna to pay for ante handler gas + taxByUsdDenom := initialization.TaxRate.MulInt(transferAmount.QuoRaw(2)). + // convert terra denom to usd denom base on gas price + Mul(initialization.UsdGasPrice.Quo(initialization.TerraGasPrice)). + TruncateInt() + feeByUsdDenom := sdk.NewCoin(initialization.UsdDenom, taxByUsdDenom.AddRaw(1)) // 1 uusd to pay for post handler gas + + node.BankSend(transferCoin1.String(), test1Addr, test2Addr, []string{}, feeByTerraDenom, feeByUsdDenom) + + test1AddrTerraBalance, err := node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) + s.Require().NoError(err) + test1AddrUsdBalance, err := node.QuerySpecificBalance(test1Addr, initialization.UsdDenom) + s.Require().NoError(err) + test2AddrTerraBalance, err := node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) + s.Require().NoError(err) + + // Final denom will be paid by both uluna and uusd + s.Require().Equal(test2AddrTerraBalance, transferCoin1) + s.Require().Equal(test1AddrTerraBalance, transferCoin1.Sub(feeByTerraDenom)) + s.Require().Equal(test1AddrUsdBalance, transferCoin2.Sub(feeByUsdDenom)) +} + +func (s *IntegrationTestSuite) TestFeeTaxForwardWasm() { + chain := s.configurer.GetChainConfig(0) + node, err := chain.GetDefaultNode() + s.Require().NoError(err) + + transferAmount1 := sdkmath.NewInt(700000000) + transferCoin1 := sdk.NewCoin(initialization.TerraDenom, transferAmount1) + + test1Addr := node.CreateWallet("test1-forward-wasm") + test2Addr := node.CreateWallet("test2-forward-wasm") + + validatorAddr := node.GetWallet(initialization.ValidatorWalletName) + + node.BankSend(transferCoin1.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + + // Test 1: User ----(execute contract with funds)---> Contract ---(execute bank send msg)---> Another User + node.StoreWasmCode("forwarder.wasm", initialization.ValidatorWalletName) + + chain.LatestCodeID = int(node.QueryLatestWasmCodeID()) + node.InstantiateWasmContract( + strconv.Itoa(chain.LatestCodeID), + `{}`, "", + initialization.ValidatorWalletName, []string{}, sdk.NewCoin(initialization.TerraDenom, sdk.NewInt(2))) + + contracts, err := node.QueryContractsFromID(chain.LatestCodeID) + s.NoError(err) + s.Len(contracts, 1, "Wrong number of contracts for the counter") + contract1Addr := contracts[0] + + transferAmount2 := sdkmath.NewInt(100000000) + transferCoin2 := sdk.NewCoin(initialization.TerraDenom, transferAmount2) + node.WasmExecute( + contract1Addr, + fmt.Sprintf(`{"forward": {"recipient": "%s"}}`, test2Addr), + transferCoin2.String(), + "test1-forward-wasm", + []string{initialization.TerraDenom}, + ) + + test1AddrBalance, err := node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) + s.Require().NoError(err) + test2AddrBalance, err := node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) + s.Require().NoError(err) + + s.Require().Equal(test2AddrBalance, transferCoin2) + s.Require().Equal(test1AddrBalance.Amount, transferAmount1.Sub(transferAmount2). + // User 1 will paid 2 times on taxes due to the contract execute bank send msg + // 2uluna will be used for gas + Sub(initialization.TaxRate.MulInt(transferAmount2.MulRaw(2)).TruncateInt()).SubRaw(2)) + + // Test 2: Contract trigger another contract's execute msg + node.InstantiateWasmContract( + strconv.Itoa(chain.LatestCodeID), + `{}`, "", + initialization.ValidatorWalletName, []string{}, sdk.NewCoin(initialization.TerraDenom, sdk.NewInt(2))) + + contracts, err = node.QueryContractsFromID(chain.LatestCodeID) + s.NoError(err) + s.Len(contracts, 2, "Wrong number of contracts for the counter") + contract2Addr := contracts[1] + + node.WasmExecute( + contract1Addr, + fmt.Sprintf(`{"forward_to_contract": {"contract": "%s", "recipient": "%s"}}`, contract2Addr, test2Addr), + transferCoin2.String(), + "test1-forward-wasm", + []string{initialization.TerraDenom}, + ) + + newTest1AddrBalance, err := node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) + s.Require().NoError(err) + newTest2AddrBalance, err := node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) + s.Require().NoError(err) + + s.Require().Equal(newTest2AddrBalance, test2AddrBalance.Add(transferCoin2)) + s.Require().Equal(newTest1AddrBalance.Amount, test1AddrBalance.Amount.Sub(transferAmount2). + // User 1 will paid 3 times on taxes: execute contract1 msg, contract 1 execute contract 2 msg, contract 2 execute bank msg + // 2uluna will be used for gas + Sub(initialization.TaxRate.MulInt(transferAmount2.MulRaw(3)).TruncateInt()).SubRaw(2)) + + // Test 3: Error when forward tx + test1AddrBalance = newTest1AddrBalance + test2AddrBalance = newTest2AddrBalance + + node.WasmExecuteError( + contract1Addr, + fmt.Sprintf(`{"forward_to_cause_error": {"contract": "%s"}}`, contract2Addr), + transferCoin2.String(), + "test1-forward-wasm", + []string{initialization.TerraDenom}, + ) + + newTest1AddrBalance, err = node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) + s.Require().NoError(err) + newTest2AddrBalance, err = node.QuerySpecificBalance(test2Addr, initialization.TerraDenom) + s.Require().NoError(err) + + s.Require().Equal(newTest2AddrBalance, test2AddrBalance) + // Transfer amount will we return + s.Require().Equal(newTest1AddrBalance.Amount, test1AddrBalance.Amount) +} + +func (s *IntegrationTestSuite) TestFeeTaxNotAcceptDenom() { + chain := s.configurer.GetChainConfig(0) + node, err := chain.GetDefaultNode() + s.Require().NoError(err) + + transferAmount1 := sdkmath.NewInt(500000000) + transferCoin1TerraDenom := sdk.NewCoin(initialization.TerraDenom, transferAmount1) + transferCoin1NonValueDenom := sdk.NewCoin(initialization.NonValueDenom, transferAmount1) + + test1Addr := node.CreateWallet("test1-not-accept-denom") + test2Addr := node.CreateWallet("test2-not-accept-denom") + + validatorAddr := node.GetWallet(initialization.ValidatorWalletName) + + node.BankSend(transferCoin1TerraDenom.String(), validatorAddr, test1Addr, []string{initialization.TerraDenom}) + + node.BankSend(transferCoin1NonValueDenom.String(), validatorAddr, test1Addr, []string{}, sdk.NewCoin(initialization.TerraDenom, sdkmath.NewInt(10))) + + // Test 1: Try to pay tx fee with non-value denom + transferAmount2 := sdkmath.NewInt(100000000) + transferCoin2 := sdk.NewCoin(initialization.TerraDenom, transferAmount2) + + gasLimit := transferAmount2.MulRaw(initialization.E10).String() + fees := sdk.NewCoins(sdk.NewCoin(initialization.NonValueDenom, transferAmount2)) + err = fmt.Errorf("can't find coin that matches") + // Tx will cause error cause it doesn't have the correct fees to pay for tx + node.BankSendError(transferCoin2.String(), test1Addr, test2Addr, "test1-not-accept-denom", gasLimit, fees, err.Error()) + + // Test 2: Try to trick the chain by paying with both uluna and non-value denom + + feeTerra := initialization.TaxRate.MulInt(transferAmount2).TruncateInt().AddRaw(2) + feeTerraCoin := sdk.NewCoin(initialization.TerraDenom, feeTerra) + fees = sdk.NewCoins(sdk.NewCoin(initialization.NonValueDenom, transferAmount2), feeTerraCoin) + + // At this time, the tx will ignore non-value denom and only deduct the uluna + node.BankSendWithWallet(transferCoin2.String(), test1Addr, test2Addr, "test1-not-accept-denom", []string{}, fees...) + + balanceTest1Terra, err := node.QuerySpecificBalance(test1Addr, initialization.TerraDenom) + s.Require().NoError(err) + balanceTest1NonValueDenom, err := node.QuerySpecificBalance(test1Addr, initialization.NonValueDenom) + s.Require().NoError(err) + + s.Require().Equal(balanceTest1Terra.Amount, transferAmount1.Sub(transferAmount2).Sub(feeTerra)) + s.Require().Equal(balanceTest1NonValueDenom.Amount, transferAmount1) } diff --git a/tests/e2e/initialization/config.go b/tests/e2e/initialization/config.go index 79a5bac58..9bd565699 100644 --- a/tests/e2e/initialization/config.go +++ b/tests/e2e/initialization/config.go @@ -12,6 +12,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" @@ -22,6 +23,7 @@ import ( govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/classic-terra/core/v3/tests/e2e/util" + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" treasurytypes "github.com/classic-terra/core/v3/x/treasury/types" ) @@ -42,19 +44,27 @@ type NodeConfig struct { const ( // common TerraDenom = "uluna" - AtomDenom = "uatom" + UsdDenom = "uusd" + EurDenom = "ueur" + NonValueDenom = "nonvalue" TerraIBCDenom = "ibc/4627AD2524E3E0523047E35BB76CC90E37D9D57ACF14F0FCBCEB2480705F3CB8" - MinGasPrice = "0.000" + MinGasPrice = "0.00000000001" + E10 = 10000000000 IbcSendAmount = 3300000000 ValidatorWalletName = "val" // chainA - ChainAID = "terra-test-a" - TerraBalanceA = 20000000000000 - StakeBalanceA = 110000000000 - StakeAmountA = 100000000000 + ChainAID = "terra-test-a" + TerraBalanceA = 200000000000000 + UsdBalanceA = 300000000000000 + EurBalanceA = 400000000000000 + NonValueBalanceA = 10000000000000 + StakeBalanceA = 110000000000 + StakeAmountA = 100000000000 // chainB ChainBID = "terra-test-b" TerraBalanceB = 500000000000 + UsdBalanceB = 60000000000000 + EurBalanceB = 40000000000000 StakeBalanceB = 440000000000 StakeAmountB = 400000000000 GenesisFeeBalance = 100000000000 @@ -72,16 +82,21 @@ var ( StakeAmountIntB = sdk.NewInt(StakeAmountB) StakeAmountCoinB = sdk.NewCoin(TerraDenom, StakeAmountIntB) - InitBalanceStrA = fmt.Sprintf("%d%s", TerraBalanceA, TerraDenom) - InitBalanceStrB = fmt.Sprintf("%d%s", TerraBalanceB, TerraDenom) + InitBalanceStrA = fmt.Sprintf("%d%s,%d%s,%d%s,%d%s", TerraBalanceA, TerraDenom, UsdBalanceA, UsdDenom, EurBalanceA, EurDenom, NonValueBalanceA, NonValueDenom) + InitBalanceStrB = fmt.Sprintf("%d%s,%d%s,%d%s", TerraBalanceB, TerraDenom, UsdBalanceB, UsdDenom, EurBalanceB, EurDenom) // InitBalanceStrC = fmt.Sprintf("%d%s", TerraBalanceC, TerraDenom) LunaToken = sdk.NewInt64Coin(TerraDenom, IbcSendAmount) // 3,300luna tenTerra = sdk.Coins{sdk.NewInt64Coin(TerraDenom, 10_000_000)} - OneMin = time.Minute // nolint - TwoMin = 2 * time.Minute // nolint - FiveMin = 5 * time.Minute // nolint - TaxRate = sdk.NewDecWithPrec(2, 2) // 0.02 + TerraGasPrice = sdk.NewDecWithPrec(5, 11) // 0.5 * 10^-10 + UsdGasPrice = sdk.NewDecWithPrec(1, 10) // 1 * 10^-10 + EurGasPrice = sdk.NewDecWithPrec(2, 10) // 2 * 10^-10 + + OneMin = time.Minute // nolint + TwoMin = 2 * time.Minute // nolint + FiveMin = 5 * time.Minute // nolint + TaxRate = sdk.NewDecWithPrec(2, 2) // 0.02 + GasAdjustment = sdk.NewDecWithPrec(12, 1) ) func addAccount(path, moniker, amountStr string, accAddr sdk.AccAddress, forkHeight int) error { @@ -241,6 +256,11 @@ func initGenesis(chain *internalChain, forkHeight int) error { return err } + err = updateModuleGenesis(appGenState, distrtypes.ModuleName, &distrtypes.GenesisState{}, updateDistrGenesis) + if err != nil { + return err + } + err = updateModuleGenesis(appGenState, treasurytypes.ModuleName, &treasurytypes.GenesisState{}, updateTreasuryGenesis) if err != nil { return err @@ -256,6 +276,11 @@ func initGenesis(chain *internalChain, forkHeight int) error { return err } + err = updateModuleGenesis(appGenState, tax2gastypes.ModuleName, &tax2gastypes.GenesisState{}, updateTax2GasGenesis) + if err != nil { + return err + } + bz, err := json.MarshalIndent(appGenState, "", " ") if err != nil { return err @@ -282,7 +307,7 @@ func updateMintGenesis(mintGenState *minttypes.GenesisState) { } func updateBankGenesis(bankGenState *banktypes.GenesisState) { - denomsToRegister := []string{TerraDenom, AtomDenom} + denomsToRegister := []string{TerraDenom, UsdDenom, EurDenom} for _, denom := range denomsToRegister { setDenomMetadata(bankGenState, denom) } @@ -303,6 +328,11 @@ func updateCrisisGenesis(crisisGenState *crisistypes.GenesisState) { crisisGenState.ConstantFee.Denom = TerraDenom } +func updateDistrGenesis(distrGenState *distrtypes.GenesisState) { + distrGenState.Params.CommunityTax = sdk.NewDecWithPrec(2, 2) + distrGenState.Params.WithdrawAddrEnabled = true +} + func updateTreasuryGenesis(treasuryGenState *treasurytypes.GenesisState) { treasuryGenState.TaxRate = TaxRate treasuryGenState.Params.TaxPolicy = treasurytypes.PolicyConstraints{ @@ -352,6 +382,17 @@ func updateGenUtilGenesis(c *internalChain) func(*genutiltypes.GenesisState) { } } +func updateTax2GasGenesis(tax2gasGenState *tax2gastypes.GenesisState) { + tax2gasGenState.Params.GasPrices = sdk.NewDecCoins( + // Gas prices will be very small so that normal tx only care about taxes + sdk.NewDecCoinFromDec("uluna", TerraGasPrice), + sdk.NewDecCoinFromDec("uusd", UsdGasPrice), + sdk.NewDecCoinFromDec("ueur", EurGasPrice), + ) + tax2gasGenState.Params.BurnTaxRate = TaxRate + tax2gasGenState.Params.Enabled = true +} + func setDenomMetadata(genState *banktypes.GenesisState, denom string) { genState.DenomMetadata = append(genState.DenomMetadata, banktypes.Metadata{ Description: fmt.Sprintf("Registered denom %s for e2e testing", denom), diff --git a/tests/e2e/scripts/forwarder.wasm b/tests/e2e/scripts/forwarder.wasm new file mode 100644 index 000000000..7a45a2e9d Binary files /dev/null and b/tests/e2e/scripts/forwarder.wasm differ diff --git a/tests/e2e/scripts/hermes_bootstrap.sh b/tests/e2e/scripts/hermes_bootstrap.sh index 0402f93ea..3e627f7e9 100644 --- a/tests/e2e/scripts/hermes_bootstrap.sh +++ b/tests/e2e/scripts/hermes_bootstrap.sh @@ -42,8 +42,8 @@ account_prefix = 'terra' key_name = 'val01-terra-a' store_prefix = 'ibc' max_gas = 6000000 -gas_price = { price = 0.1, denom = 'uluna' } -gas_multiplier = 1.1 +gas_price = { price = 1, denom = 'uluna' } +gas_multiplier = 3 max_msg_num = 30 max_tx_size = 2097152 clock_drift = '5s' # to accomdate docker containers @@ -69,8 +69,8 @@ account_prefix = 'terra' key_name = 'val01-terra-b' store_prefix = 'ibc' max_gas = 6000000 -gas_price = { price = 0.1, denom = 'uluna' } -gas_multiplier = 1.1 +gas_price = { price = 1, denom = 'uluna' } +gas_multiplier = 3 max_msg_num = 30 max_tx_size = 2097152 clock_drift = '5s' # to accomdate docker containers diff --git a/tests/interchaintest/setup.go b/tests/interchaintest/setup.go index 336c0b389..00b826307 100644 --- a/tests/interchaintest/setup.go +++ b/tests/interchaintest/setup.go @@ -81,6 +81,10 @@ func ModifyGenesis() func(ibc.ChainConfig, []byte) ([]byte, error) { if err := dyno.Set(g, chainConfig.Denom, "app_state", "gov", "params", "min_deposit", 0, "denom"); err != nil { return nil, fmt.Errorf("failed to set voting period in genesis json: %w", err) } + // Disable tax2gas params to disable + if err := dyno.Set(g, false, "app_state", "tax2gas", "params", "enabled"); err != nil { + return nil, fmt.Errorf("failed to set tax2gas params in genesis json: %w", err) + } // Modify signed blocks window if err := dyno.Set(g, signedBlocksWindow, "app_state", "slashing", "params", "signed_blocks_window"); err != nil { return nil, fmt.Errorf("failed to set signed blocks window in genesis json: %w", err) diff --git a/x/tax2gas/README.md b/x/tax2gas/README.md new file mode 100644 index 000000000..3fcd04783 --- /dev/null +++ b/x/tax2gas/README.md @@ -0,0 +1,27 @@ +# Tax2gas + +## Testcases + +- Normal tx success +- Not supported tx will not be deducted tax amount +- Special IBC tx will be bypass when gas usage is not exceeded +- Forward tx should minus the amount to tx origin +- Multiple forward works +- Error forward tx should return the fund +- Out of gas should return the tax and not consumed gas +- Grant msg should work +- Allow pay with multiple fees should work +- Try to pay with non value token denom should fail + +| No | Name | Scenario | Expect Result | Covered by | +|----|----------|-------------------|---------------|------------| +| 1 | Normal transaction should success | User transfer or make some special transactions which send coins to different address | Tax should be deducted with correct amount| [TestFeeTax](../../tests/e2e/e2e_test.go#L108)
[TestFeeTaxWasm](../../tests/e2e/e2e_test.go#L158)| +| 2 | Not supported tx will not be deducted tax amount | User transfer or make some special transactions that not in the tax list | Tax shouldn't be deducted with correct amount| [TestFeeTaxNotSupport](../../tests/e2e/e2e_test.go#L306) | +| 3 | Special IBC tx will be bypass when gas limit is not exceeded | User make IBC transactions that happen both cases:
- Gas usage does not exceeded `maxTotalBypassMinFeeMsgGasUsage`
-Gas usage exceeded `maxTotalBypassMinFeeMsgGasUsage` | Bypass when gas limit not exceeded and deduct fee when exceed | 🛑 Not figure out the way to make update client in e2e, should be test in testnet | +| 4 | Forward transaction should deduct the amount to tx origin | User execute contract that will trigger an execute msg to another contract | - User should be the tx origin of the execute msg
- Tax should be deducted with correct amount | [TestFeeTaxForwardWasm](../../tests/e2e/e2e_test.go#L428) | +| 5 | Multiple forward works | Contracts will trigger another contracts multiple times | - User should be the tx origin of the execute msg
- Tax should be deducted with correct amount | [TestFeeTaxForwardWasm](../../tests/e2e/e2e_test.go#L428) | +| 6 | Error forward tx should return the tax and not consumed gas | User execute contract that will trigger an execute msg to another contract. The execute msg to another contract will be failed | Tax and not consumed gas should be revert to user | [TestFeeTaxForwardWasm](../../tests/e2e/e2e_test.go#L428) | +| 7 | Out of gas should return the tax and not consumed gas | User make some transactions with limited gas amount that will lead to cause `out of gas` error | Tax and not consumed gas should be revert to user | 🛑 Not figure out the way to make `out of gas` error occur, should be test in testnet | +| 8 | Grant msg should work | User grant multiple type of permissions to different transactions | Grant permission msg will only can deduct one denom in ante handler and one denom in post hanlder | [TestFeeTaxGrant](../../tests/e2e/e2e_test.go#L214) | +| 9 | Allow pay with multiple fees should work | User make transaction with multiple coins as fee | Fee can be paid by multiple denom, if one denom is not enough, then it will deduct other denom | [TestFeeTaxMultipleDenoms](../../tests/e2e/e2e_test.go#L380) | +| 10 | Try to pay with non value token denom should fail | User make transaction that use a different denom as fee | That denom should be reject and the tx should only accept denom listed in params | [TestFeeTaxNotAcceptDenom](../../tests/e2e/e2e_test.go#L531) | \ No newline at end of file diff --git a/x/tax2gas/ante/ante.go b/x/tax2gas/ante/ante.go new file mode 100644 index 000000000..1621943b8 --- /dev/null +++ b/x/tax2gas/ante/ante.go @@ -0,0 +1,267 @@ +package ante + +import ( + "fmt" + "math" + + tmstrings "github.com/cometbft/cometbft/libs/strings" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + 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" +) + +// FeeDecorator deducts fees from the first signer of the tx +// If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error +// Call next AnteHandler if fees successfully deducted +// CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator +type FeeDecorator struct { + accountKeeper ante.AccountKeeper + bankKeeper types.BankKeeper + feegrantKeeper types.FeegrantKeeper + treasuryKeeper types.TreasuryKeeper + tax2gasKeeper tax2gasKeeper.Keeper +} + +func NewFeeDecorator(ak ante.AccountKeeper, bk types.BankKeeper, fk types.FeegrantKeeper, tk types.TreasuryKeeper, taxKeeper tax2gasKeeper.Keeper) FeeDecorator { + return FeeDecorator{ + accountKeeper: ak, + bankKeeper: bk, + feegrantKeeper: fk, + treasuryKeeper: tk, + tax2gasKeeper: taxKeeper, + } +} + +func (fd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + } + + if !simulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 { + return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas") + } + + var ( + priority int64 + err error + ) + + msgs := feeTx.GetMsgs() + if tax2gasutils.IsOracleTx(msgs) || !fd.tax2gasKeeper.IsEnabled(ctx) { + return next(ctx, tx, simulate) + } + + // Check if the gas price node set is larger than the current gas price + // it will be the new gas price + gasPrices := fd.GetFinalGasPrices(ctx) + // Compute taxes based on consumed gas + gasConsumed := ctx.GasMeter().GasConsumed() + gasConsumedFees, err := tax2gasutils.ComputeFeesOnGasConsumed(tx, gasPrices, sdk.NewInt(int64(gasConsumed))) + if err != nil { + return ctx, err + } + + // Compute taxes based on sent amount + burnTaxRate := fd.tax2gasKeeper.GetBurnTaxRate(ctx) + taxes := tax2gasutils.FilterMsgAndComputeTax(ctx, fd.treasuryKeeper, burnTaxRate, msgs...) + // Convert taxes to gas + taxGas, err := tax2gasutils.ComputeGas(gasPrices, taxes) + if err != nil { + return ctx, err + } + + // Bypass min fee requires: + // - the tx contains only message types that can bypass the minimum fee, + // see BypassMinFeeMsgTypes; + // - the total gas limit per message does not exceed MaxTotalBypassMinFeeMsgGasUsage, + // i.e., totalGas <= MaxTotalBypassMinFeeMsgGasUsage + // Otherwise, minimum fees and global fees are checked to prevent spam. + maxTotalBypassMinFeeMsgGasUsage := fd.tax2gasKeeper.GetMaxTotalBypassMinFeeMsgGasUsage(ctx) + doesNotExceedMaxGasUsage := feeTx.GetGas() <= maxTotalBypassMinFeeMsgGasUsage + allBypassMsgs := fd.ContainsOnlyBypassMinFeeMsgs(ctx, msgs) + allowedToBypassMinFee := allBypassMsgs && doesNotExceedMaxGasUsage + + if allowedToBypassMinFee { + return next(ctx, tx, simulate) + } + + if !simulate { + isOracleTx := tax2gasutils.IsOracleTx(msgs) + // the priority to be added in mempool is based on + // the tax gas that user need to pay + priority = int64(math.MaxInt64) + if !isOracleTx { + if taxGas.IsInt64() { + priority = taxGas.Int64() + } + } + } + + // Try to deduct the gasConsumed fees + paidDenom, err := fd.tryDeductFee(ctx, feeTx, gasConsumedFees, simulate) + if err != nil { + return ctx, err + } + + newCtx := ctx.WithPriority(priority). + WithValue(types.TaxGas, taxGas). + WithValue(types.FinalGasPrices, gasPrices) + if !taxGas.IsZero() { + newCtx.TaxGasMeter().ConsumeGas(taxGas, "ante handler taxGas") + } + newCtx = newCtx.WithValue(types.AnteConsumedGas, gasConsumed) + if paidDenom != "" { + newCtx = newCtx.WithValue(types.PaidDenom, paidDenom) + } + + return next(newCtx, tx, simulate) +} + +func (fd FeeDecorator) tryDeductFee(ctx sdk.Context, feeTx sdk.FeeTx, taxes sdk.Coins, simulate bool) (string, error) { + if addr := fd.accountKeeper.GetModuleAddress(authtypes.FeeCollectorName); addr == nil { + return "", fmt.Errorf("fee collector module account (%s) has not been set", authtypes.FeeCollectorName) + } + + feeCoins := feeTx.GetFee() + feePayer := feeTx.FeePayer() + feeGranter := feeTx.FeeGranter() + deductFeesFrom := feePayer + + foundCoins := sdk.Coins{} + if !taxes.IsZero() { + for _, coin := range feeCoins { + found, requiredFee := taxes.Find(coin.Denom) + if !found { + continue + } + if coin.Amount.GTE(requiredFee.Amount) { + foundCoins = foundCoins.Add(requiredFee) + } + } + } else { + return "", nil + } + + // if feegranter set deduct fee from feegranter account. + // this works with only when feegrant enabled. + if feeGranter != nil { + if fd.feegrantKeeper == nil { + return "", sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled") + } else if !feeGranter.Equals(feePayer) { + allowance, err := fd.feegrantKeeper.GetAllowance(ctx, feeGranter, feePayer) + if err != nil { + 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, foundCoins, 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) + } + } + + deductFeesFrom = feeGranter + } + + deductFeesFromAcc := fd.accountKeeper.GetAccount(ctx, deductFeesFrom) + if deductFeesFromAcc == nil { + return "", sdkerrors.ErrUnknownAddress.Wrapf("fee payer address: %s does not exist", deductFeesFrom) + } + + // deduct the fees + if !foundCoins.IsZero() { + foundCoins, err := DeductFees(fd.bankKeeper, ctx, deductFeesFromAcc, foundCoins) + if err != nil { + return "", err + } + + events := sdk.Events{ + sdk.NewEvent( + sdk.EventTypeTx, + sdk.NewAttribute(sdk.AttributeKeyFee, foundCoins.String()), + sdk.NewAttribute(sdk.AttributeKeyFeePayer, deductFeesFrom.String()), + ), + } + ctx.EventManager().EmitEvents(events) + + // As there is only 1 element + return foundCoins.Denoms()[0], nil + } + if simulate { + return "", nil + } + return "", fmt.Errorf("can't find coin that matches. Expected %q, wanted %q", feeCoins, taxes) +} + +// DeductFees deducts fees from the given account. +func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc authtypes.AccountI, fees sdk.Coins) (sdk.Coins, error) { + if !fees.IsValid() { + return nil, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees) + } + + 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, sdkerrors.ErrInsufficientFunds +} + +func (fd FeeDecorator) ContainsOnlyBypassMinFeeMsgs(ctx sdk.Context, msgs []sdk.Msg) bool { + bypassMsgTypes := fd.tax2gasKeeper.GetBypassMinFeeMsgTypes(ctx) + + for _, msg := range msgs { + if tmstrings.StringInSlice(sdk.MsgTypeURL(msg), bypassMsgTypes) { + continue + } + return false + } + + return true +} + +func (fd FeeDecorator) GetFinalGasPrices(ctx sdk.Context) sdk.DecCoins { + tax2gasGasPrices := fd.tax2gasKeeper.GetGasPrices(ctx) + minGasPrices := ctx.MinGasPrices() + gasPrices := make(sdk.DecCoins, len(tax2gasGasPrices)) + + for i, gasPrice := range tax2gasGasPrices { + maxGasPrice := sdk.DecCoin{ + Denom: gasPrice.Denom, + Amount: sdk.MaxDec( + minGasPrices.AmountOf(gasPrice.Denom), + gasPrice.Amount, + ), + } + + gasPrices[i] = maxGasPrice + } + + return gasPrices +} diff --git a/x/tax2gas/ante/ante_test.go b/x/tax2gas/ante/ante_test.go new file mode 100644 index 000000000..ca2f73318 --- /dev/null +++ b/x/tax2gas/ante/ante_test.go @@ -0,0 +1,133 @@ +package ante_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + dbm "github.com/cometbft/cometbft-db" + "github.com/cometbft/cometbft/libs/log" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/tx" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + + terraapp "github.com/classic-terra/core/v3/app" + tax2gastypes "github.com/classic-terra/core/v3/x/tax2gas/types" + treasurytypes "github.com/classic-terra/core/v3/x/treasury/types" +) + +type AnteTestSuite struct { + suite.Suite + + app *terraapp.TerraApp + // anteHandler sdk.AnteHandler + ctx sdk.Context + clientCtx client.Context + txBuilder client.TxBuilder +} + +// returns context and app with params set on account keeper +func createTestApp(isCheckTx bool, tempDir string) (*terraapp.TerraApp, sdk.Context) { + // TODO: we need to feed in custom binding here? + var wasmOpts []wasmkeeper.Option + app := terraapp.NewTerraApp( + log.NewNopLogger(), dbm.NewMemDB(), nil, true, map[int64]bool{}, + tempDir, terraapp.MakeEncodingConfig(), + simtestutil.EmptyAppOptions{}, wasmOpts, + ) + ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) + app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) + app.TreasuryKeeper.SetParams(ctx, treasurytypes.DefaultParams()) + app.DistrKeeper.SetParams(ctx, distributiontypes.DefaultParams()) + app.DistrKeeper.SetFeePool(ctx, distributiontypes.InitialFeePool()) + tax2gasParams := tax2gastypes.DefaultParams() + tax2gasParams.Enabled = true + app.Tax2gasKeeper.SetParams(ctx, tax2gasParams) + + return app, ctx +} + +// SetupTest setups a new test, with new app, context, and anteHandler. +func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { + tempDir := suite.T().TempDir() + suite.app, suite.ctx = createTestApp(isCheckTx, tempDir) + suite.ctx = suite.ctx.WithBlockHeight(1) + + // Set up TxConfig. + encodingConfig := suite.SetupEncoding() + + suite.clientCtx = client.Context{}. + WithTxConfig(encodingConfig.TxConfig) +} + +func (suite *AnteTestSuite) SetupEncoding() testutil.TestEncodingConfig { + encodingConfig := testutil.MakeTestEncodingConfig() + // We're using TestMsg encoding in some tests, so register it here. + encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil) + testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + return encodingConfig +} + +// CreateTestTx is a helper function to create a tx given multiple inputs. +func (suite *AnteTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) { + // First round: we gather all the signer infos. We use the "set empty + // signature" hack to do that. + var sigsV2 []signing.SignatureV2 + for i, priv := range privs { + sigV2 := signing.SignatureV2{ + PubKey: priv.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: accSeqs[i], + } + + sigsV2 = append(sigsV2, sigV2) + } + err := suite.txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + // Second round: all signer infos are set, so each signer can sign. + sigsV2 = []signing.SignatureV2{} + for i, priv := range privs { + signerData := xauthsigning.SignerData{ + ChainID: chainID, + AccountNumber: accNums[i], + Sequence: accSeqs[i], + } + sigV2, err := tx.SignWithPrivKey( + suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), signerData, + suite.txBuilder, priv, suite.clientCtx.TxConfig, accSeqs[i]) + if err != nil { + return nil, err + } + + sigsV2 = append(sigsV2, sigV2) + } + err = suite.txBuilder.SetSignatures(sigsV2...) + if err != nil { + return nil, err + } + + return suite.txBuilder.GetTx(), nil +} + +func TestAnteTestSuite(t *testing.T) { + suite.Run(t, new(AnteTestSuite)) +} diff --git a/x/tax2gas/ante/fee_test.go b/x/tax2gas/ante/fee_test.go new file mode 100644 index 000000000..d6320e3cc --- /dev/null +++ b/x/tax2gas/ante/fee_test.go @@ -0,0 +1,666 @@ +package ante_test + +import ( + "encoding/json" + "fmt" + "os" + "time" + + "cosmossdk.io/math" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + authz "github.com/cosmos/cosmos-sdk/x/authz" + "github.com/cosmos/cosmos-sdk/x/bank/testutil" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + core "github.com/classic-terra/core/v3/types" + markettypes "github.com/classic-terra/core/v3/x/market/types" + oracletypes "github.com/classic-terra/core/v3/x/oracle/types" + "github.com/classic-terra/core/v3/x/tax2gas/ante" + + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" + ibctesting "github.com/cosmos/ibc-go/v7/testing" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" +) + +var ( + sendCoin = sdk.NewInt64Coin(core.MicroLunaDenom, int64(1000000)) + sendCoins = sdk.NewCoins(sendCoin) +) + +func (s *AnteTestSuite) TestDeductFeeDecorator() { + s.SetupTest(true) // setup + s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() + + mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.Tax2gasKeeper) + antehandler := sdk.ChainAnteDecorators(mfd) + + // keys and addresses + priv1, _, addr1 := testdata.KeyTestPubAddr() + coins := sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(300))) + testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, coins) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} + tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) + s.Require().NoError(err) + + testCases := []struct { + name string + simulation bool + checkTx bool + mallate func() + expFail bool + expErrMsg string + }{ + { + name: "success: zero gas in simulation", + simulation: true, + checkTx: true, + mallate: func() { + // set zero gas + s.txBuilder.SetGasLimit(0) + }, + expFail: false, + }, + { + name: "Success: deduct sufficient fees", + simulation: false, + checkTx: true, + mallate: func() { + msg := testdata.NewTestMsg(addr1) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + // GasConsumed : 7328*28.325 = 207566 + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(207566)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 207566)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(7328) + }, + expFail: false, + }, + { + name: "Fail: deduct insufficient fees", + simulation: false, + checkTx: true, + mallate: func() { + msg := testdata.NewTestMsg(addr1) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + // GasConsumed : 7328*28,325 = 207566 + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(207565)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 207565)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(7328) + }, + expFail: true, + expErrMsg: "can't find coin that matches", + }, + { + name: "Success: Instantiate contract", + simulation: false, + checkTx: true, + mallate: func() { + msg := &wasmtypes.MsgInstantiateContract{ + Sender: addr1.String(), + Admin: addr1.String(), + CodeID: 0, + Msg: []byte{}, + Funds: sendCoins, + } + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 1000 (tax) = 2833500 + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2833500)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Success: Instantiate2 contract", + simulation: false, + checkTx: true, + mallate: func() { + msg := &wasmtypes.MsgInstantiateContract2{ + Sender: addr1.String(), + Admin: addr1.String(), + CodeID: 0, + Msg: []byte{}, + Funds: sendCoins, + } + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 1000 (tax) = 2833500 + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2833500)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Fail: Instantiate2 contract insufficient fees", + simulation: false, + checkTx: true, + mallate: func() { + msg := &wasmtypes.MsgInstantiateContract2{ + Sender: addr1.String(), + Admin: addr1.String(), + CodeID: 0, + Msg: []byte{}, + Funds: sendCoins, + } + // Consumed gas at the point of ante is: 7220 + // 7220*28.325 (gas fee) = 207566 + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833499)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 207565)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: true, + expErrMsg: "can't find coin that matches", + }, + { + name: "Success: Execute contract", + simulation: false, + checkTx: true, + mallate: func() { + msg := &wasmtypes.MsgExecuteContract{ + Sender: addr1.String(), + Contract: addr1.String(), + Msg: []byte{}, + Funds: sendCoins, + } + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 1000 (tax) = 2833500 + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2833500)) + + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Success: Bank send", + simulation: false, + checkTx: true, + mallate: func() { + msg := banktypes.NewMsgSend(addr1, addr1, sendCoins) + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 1000 (tax) = 2833500 + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2833500)) + + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Success: Bank multisend", + simulation: false, + checkTx: true, + mallate: func() { + msg := banktypes.NewMsgMultiSend( + []banktypes.Input{ + banktypes.NewInput(addr1, sendCoins), + banktypes.NewInput(addr1, sendCoins), + }, + []banktypes.Output{ + banktypes.NewOutput(addr1, sendCoins), + banktypes.NewOutput(addr1, sendCoins), + }) + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 2000 (tax) = 2834500 + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2834500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2834500)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Success: Market swapsend", + simulation: false, + checkTx: true, + mallate: func() { + msg := markettypes.NewMsgSwapSend(addr1, addr1, sendCoin, core.MicroKRWDenom) + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 1000 (tax) = 2833500 + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2833500)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Success: Authz exec", + simulation: false, + checkTx: true, + mallate: func() { + msg := authz.NewMsgExec(addr1, []sdk.Msg{banktypes.NewMsgSend(addr1, addr1, sendCoins)}) + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 1000 (tax) = 2833500 + s.Require().NoError(s.txBuilder.SetMsgs(&msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2833500)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Fail: Authz exec", + simulation: false, + checkTx: true, + mallate: func() { + msg := authz.NewMsgExec(addr1, []sdk.Msg{banktypes.NewMsgSend(addr1, addr1, sendCoins)}) + // Consumed gas at the point of ante is: 7220 but the gas limit is 100000 + // 100000*28.325 (gas fee) + 1000 (tax) = 2833500 + s.Require().NoError(s.txBuilder.SetMsgs(&msg)) + err = testutil.FundAccount(s.app.BankKeeper, s.ctx, addr1, sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(2833500)))) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 2833500)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(100000) + }, + expFail: false, + }, + { + name: "Bypass: ibc MsgRecvPacket", + simulation: false, + checkTx: true, + mallate: func() { + msg := ibcchanneltypes.NewMsgRecvPacket( + ibcchanneltypes.Packet{}, + []byte(""), + ibcclienttypes.ZeroHeight(), + addr1.String(), + ) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_000) + }, + expFail: false, + }, + { + name: "Not Bypass: ibc MsgRecvPacket", + simulation: false, + checkTx: true, + mallate: func() { + msg := ibcchanneltypes.NewMsgRecvPacket( + ibcchanneltypes.Packet{}, + []byte(""), + ibcclienttypes.ZeroHeight(), + addr1.String(), + ) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_001) + }, + expFail: true, + expErrMsg: "can't find coin that matches", + }, + { + name: "Bypass: ibc MsgAcknowledgement", + simulation: false, + checkTx: true, + mallate: func() { + msg := ibcchanneltypes.NewMsgAcknowledgement( + ibcchanneltypes.Packet{}, + []byte(""), + []byte(""), + ibcclienttypes.ZeroHeight(), + addr1.String(), + ) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_000) + }, + expFail: false, + }, + { + name: "Bypass: ibc MsgUpdateClient", + simulation: false, + checkTx: true, + mallate: func() { + soloMachine := ibctesting.NewSolomachine(s.T(), s.app.AppCodec(), "solomachine", "", 2) + msg, err := ibcclienttypes.NewMsgUpdateClient( + soloMachine.ClientID, + soloMachine.CreateHeader(soloMachine.Diversifier), + string(addr1), + ) + s.Require().NoError(err) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_000) + }, + expFail: false, + }, + { + name: "Bypass: ibc MsgTimeout", + simulation: false, + checkTx: true, + mallate: func() { + msg := ibcchanneltypes.NewMsgTimeout( + ibcchanneltypes.Packet{}, + 1, + []byte(""), + ibcclienttypes.ZeroHeight(), + addr1.String(), + ) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_000) + }, + expFail: false, + }, + { + name: "Bypass: ibc MsgTimeoutOnClose", + simulation: false, + checkTx: true, + mallate: func() { + msg := ibcchanneltypes.NewMsgTimeoutOnClose( + ibcchanneltypes.Packet{}, + 1, + []byte(""), + []byte(""), + ibcclienttypes.ZeroHeight(), + addr1.String(), + ) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_000) + }, + expFail: false, + }, + { + name: "Other msgs must pay gas fee", + simulation: false, + checkTx: true, + mallate: func() { + msg := stakingtypes.NewMsgDelegate( + addr1, + sdk.ValAddress(addr1), + sdk.NewCoin(core.MicroLunaDenom, math.NewInt(100000)), + ) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_000) + }, + expFail: true, + expErrMsg: "can't find coin that matches", + }, + { + name: "Oracle zero fee", + simulation: false, + checkTx: true, + mallate: func() { + val, err := stakingtypes.NewValidator(sdk.ValAddress(addr1), priv1.PubKey(), stakingtypes.Description{}) + s.Require().NoError(err) + + msg := oracletypes.NewMsgAggregateExchangeRatePrevote( + oracletypes.GetAggregateVoteHash("salt", "exchange rates", val.GetOperator()), + addr1, + val.GetOperator(), + ) + s.Require().NoError(s.txBuilder.SetMsgs(msg)) + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, 0)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(1_000_000) + }, + expFail: false, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + tc.mallate() + s.ctx = s.app.BaseApp.NewContext(tc.checkTx, tmproto.Header{}) + + _, err = antehandler(s.ctx, tx, tc.simulation) + + if tc.expFail { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.expErrMsg) + } else { + s.Require().NoError(err) + } + }) + } +} + +func (s *AnteTestSuite) TestTaxExemption() { + // keys and addresses + var privs []cryptotypes.PrivKey + var addrs []sdk.AccAddress + + // 0, 1: exemption + // 2, 3: normal + for i := 0; i < 4; i++ { + priv, _, addr := testdata.KeyTestPubAddr() + privs = append(privs, priv) + addrs = append(addrs, addr) + } + + // set send amount + sendAmt := int64(1000000) + sendCoin := sdk.NewInt64Coin(core.MicroLunaDenom, sendAmt) + feeAmt := int64(1000) + + cases := []struct { + name string + msgSigner cryptotypes.PrivKey + msgCreator func() []sdk.Msg + minFeeAmount int64 + gasLimit uint64 + }{ + { + name: "MsgSend(exemption -> exemption)", + msgSigner: privs[0], + msgCreator: func() []sdk.Msg { + var msgs []sdk.Msg + + msg1 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) + msgs = append(msgs, msg1) + + return msgs + }, + // 263241*28.325 = 7456302 - only gas fee + minFeeAmount: 7456302, + gasLimit: 263241, + }, { + name: "MsgSend(normal -> normal)", + msgSigner: privs[2], + msgCreator: func() []sdk.Msg { + var msgs []sdk.Msg + + msg1 := banktypes.NewMsgSend(addrs[2], addrs[3], sdk.NewCoins(sendCoin)) + msgs = append(msgs, msg1) + + return msgs + }, + // tax this one hence burn amount is fee amount + // gasLimit * 28.325 = 8497500 + minFeeAmount: 8497500 + feeAmt, + }, { + name: "MsgExec(MsgSend(normal -> normal))", + msgSigner: privs[2], + msgCreator: func() []sdk.Msg { + var msgs []sdk.Msg + + msg1 := authz.NewMsgExec(addrs[1], []sdk.Msg{banktypes.NewMsgSend(addrs[2], addrs[3], sdk.NewCoins(sendCoin))}) + msgs = append(msgs, &msg1) + + return msgs + }, + // tax this one hence burn amount is fee amount + // gasLimit * 28.325 = 8497500 + minFeeAmount: 8497500 + feeAmt, + }, { + name: "MsgSend(exemption -> normal), MsgSend(exemption -> exemption)", + msgSigner: privs[0], + msgCreator: func() []sdk.Msg { + var msgs []sdk.Msg + + msg1 := banktypes.NewMsgSend(addrs[0], addrs[2], sdk.NewCoins(sendCoin)) + msgs = append(msgs, msg1) + msg2 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) + msgs = append(msgs, msg2) + + return msgs + }, + // tax this one hence burn amount is fee amount + // gasLimit * 28.325 = 8497500 + minFeeAmount: 8497500 + feeAmt, + }, { + name: "MsgSend(exemption -> exemption), MsgMultiSend(exemption -> normal, exemption -> exemption)", + msgSigner: privs[0], + msgCreator: func() []sdk.Msg { + var msgs []sdk.Msg + + msg1 := banktypes.NewMsgSend(addrs[0], addrs[1], sdk.NewCoins(sendCoin)) + msgs = append(msgs, msg1) + msg2 := banktypes.NewMsgMultiSend( + []banktypes.Input{ + { + Address: addrs[0].String(), + Coins: sdk.NewCoins(sendCoin), + }, + { + Address: addrs[0].String(), + Coins: sdk.NewCoins(sendCoin), + }, + }, + []banktypes.Output{ + { + Address: addrs[2].String(), + Coins: sdk.NewCoins(sendCoin), + }, + { + Address: addrs[1].String(), + Coins: sdk.NewCoins(sendCoin), + }, + }, + ) + msgs = append(msgs, msg2) + + return msgs + }, + // gasLimit * 28.325 = 8497500 + minFeeAmount: 8497500 + feeAmt*2, + }, { + name: "MsgExecuteContract(exemption), MsgExecuteContract(normal)", + msgSigner: privs[3], + msgCreator: func() []sdk.Msg { + sendAmount := int64(1000000) + sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount)) + // get wasm code for wasm contract create and instantiate + wasmCode, err := os.ReadFile("./testdata/hackatom.wasm") + s.Require().NoError(err) + per := wasmkeeper.NewDefaultPermissionKeeper(s.app.WasmKeeper) + // set wasm default params + s.app.WasmKeeper.SetParams(s.ctx, wasmtypes.DefaultParams()) + // wasm create + CodeID, _, err := per.Create(s.ctx, addrs[0], wasmCode, nil) + s.Require().NoError(err) + // params for contract init + r := wasmkeeper.HackatomExampleInitMsg{Verifier: addrs[0], Beneficiary: addrs[0]} + bz, err := json.Marshal(r) + s.Require().NoError(err) + // change block time for contract instantiate + s.ctx = s.ctx.WithBlockTime(time.Date(2020, time.April, 22, 12, 0, 0, 0, time.UTC)) + // instantiate contract then set the contract address to tax exemption + addr, _, err := per.Instantiate(s.ctx, CodeID, addrs[0], nil, bz, "my label", nil) + s.Require().NoError(err) + s.app.TreasuryKeeper.AddBurnTaxExemptionAddress(s.ctx, addr.String()) + // instantiate contract then not set to tax exemption + addr1, _, err := per.Instantiate(s.ctx, CodeID, addrs[0], nil, bz, "my label", nil) + s.Require().NoError(err) + + var msgs []sdk.Msg + // msg and signatures + msg1 := &wasmtypes.MsgExecuteContract{ + Sender: addrs[0].String(), + Contract: addr.String(), + Msg: []byte{}, + Funds: sendCoins, + } + msgs = append(msgs, msg1) + + msg2 := &wasmtypes.MsgExecuteContract{ + Sender: addrs[3].String(), + Contract: addr1.String(), + Msg: []byte{}, + Funds: sendCoins, + } + msgs = append(msgs, msg2) + return msgs + }, + // gasLimit*28.325 = 33990000 + minFeeAmount: 33990000 + feeAmt, + gasLimit: 1200000, + }, + } + + // there should be no coin in burn module + for _, c := range cases { + s.SetupTest(true) // setup + require := s.Require() + tk := s.app.TreasuryKeeper + burnSplitRate := sdk.NewDecWithPrec(5, 1) + + // Set burn split rate to 50% + tk.SetBurnSplitRate(s.ctx, burnSplitRate) + + fmt.Printf("CASE = %s \n", c.name) + s.txBuilder = s.clientCtx.TxConfig.NewTxBuilder() + + tk.AddBurnTaxExemptionAddress(s.ctx, addrs[0].String()) + tk.AddBurnTaxExemptionAddress(s.ctx, addrs[1].String()) + + mfd := ante.NewFeeDecorator(s.app.AccountKeeper, s.app.BankKeeper, s.app.FeeGrantKeeper, s.app.TreasuryKeeper, s.app.Tax2gasKeeper) + antehandler := sdk.ChainAnteDecorators(mfd) + + for i := 0; i < 4; i++ { + coins := sdk.NewCoins(sdk.NewCoin(core.MicroLunaDenom, sdk.NewInt(100000000000))) + testutil.FundAccount(s.app.BankKeeper, s.ctx, addrs[i], coins) + } + + // msg and signatures + feeAmount := sdk.NewCoins(sdk.NewInt64Coin(core.MicroLunaDenom, c.minFeeAmount)) + gasLimit := uint64(300000) + if c.gasLimit != 0 { + gasLimit = c.gasLimit + } + require.NoError(s.txBuilder.SetMsgs(c.msgCreator()...)) + s.txBuilder.SetFeeAmount(feeAmount) + s.txBuilder.SetGasLimit(gasLimit) + + privs, accNums, accSeqs := []cryptotypes.PrivKey{c.msgSigner}, []uint64{0}, []uint64{0} + tx, err := s.CreateTestTx(privs, accNums, accSeqs, s.ctx.ChainID()) + require.NoError(err) + + _, err = antehandler(s.ctx, tx, false) + require.NoError(err) + } +} diff --git a/custom/auth/ante/testdata/hackatom.wasm b/x/tax2gas/ante/testdata/hackatom.wasm similarity index 100% rename from custom/auth/ante/testdata/hackatom.wasm rename to x/tax2gas/ante/testdata/hackatom.wasm diff --git a/x/tax2gas/client/cli/query.go b/x/tax2gas/client/cli/query.go new file mode 100644 index 000000000..f3e697795 --- /dev/null +++ b/x/tax2gas/client/cli/query.go @@ -0,0 +1,89 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + tax2gasQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + tax2gasQueryCmd.AddCommand( + GetCmdQueryParams(), + GetCmdBurnTaxRate(), + ) + + return tax2gasQueryCmd +} + +// GetCmdQueryParams implements a command to return the current parameters. +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current tax2gas module parameters", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + params := &types.QueryParamsRequest{} + + res, err := queryClient.Params(context.Background(), params) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + +// GetCmdBurnTaxRate implements a command to return the current burn tax rate. +func GetCmdBurnTaxRate() *cobra.Command { + cmd := &cobra.Command{ + Use: "burn-tax-rate", + Short: "Query the current burn tax rate", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + burnTaxRate := &types.QueryBurnTaxRateRequest{} + + res, err := queryClient.BurnTaxRate(context.Background(), burnTaxRate) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/tax2gas/client/cli/tx.go b/x/tax2gas/client/cli/tx.go new file mode 100644 index 000000000..53254c471 --- /dev/null +++ b/x/tax2gas/client/cli/tx.go @@ -0,0 +1,21 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +// NewTxCmd returns a root CLI command handler for certain modules transaction commands. +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "tax2gas subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + return txCmd +} diff --git a/x/tax2gas/exported/alias.go b/x/tax2gas/exported/alias.go new file mode 100644 index 000000000..7f10c1602 --- /dev/null +++ b/x/tax2gas/exported/alias.go @@ -0,0 +1,12 @@ +// DONTCOVER +package exported + +import ( + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +var NewQueryClient = types.NewQueryClient + +type ( + QueryBurnTaxRateRequest = types.QueryBurnTaxRateRequest +) diff --git a/x/tax2gas/genesis.go b/x/tax2gas/genesis.go new file mode 100644 index 000000000..0ef46e9f7 --- /dev/null +++ b/x/tax2gas/genesis.go @@ -0,0 +1,14 @@ +package module + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/classic-terra/core/v3/x/tax2gas/keeper" + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +// InitGenesis initializes default parameters +// and the keeper's address to pubkey map +func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, data *types.GenesisState) { + keeper.SetParams(ctx, data.Params) +} diff --git a/x/tax2gas/keeper/keeper.go b/x/tax2gas/keeper/keeper.go new file mode 100644 index 000000000..45db46e59 --- /dev/null +++ b/x/tax2gas/keeper/keeper.go @@ -0,0 +1,83 @@ +package keeper + +import ( + "fmt" + + "github.com/cometbft/cometbft/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +type Keeper struct { + storeKey storetypes.StoreKey + cdc codec.BinaryCodec + + // the address capable of executing a MsgUpdateParams message. Typically, this + // should be the x/gov module account. + authority string +} + +func NewKeeper( + cdc codec.BinaryCodec, + storeKey storetypes.StoreKey, + authority string, +) Keeper { + if _, err := sdk.AccAddressFromBech32(authority); err != nil { + panic(fmt.Errorf("invalid bank authority address: %w", err)) + } + + return Keeper{cdc: cdc, storeKey: storeKey, authority: authority} +} + +// InitGenesis initializes the tax2gas module's state from a provided genesis +// state. +func (k Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) { + if err := genState.Validate(); err != nil { + panic(err) + } + + k.SetParams(ctx, genState.Params) +} + +// ExportGenesis returns the tax2gas module's exported genesis. +func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { + return &types.GenesisState{ + Params: k.GetParams(ctx), + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// GetAuthority returns the x/tax2gas module's authority. +func (k Keeper) GetAuthority() string { + return k.authority +} + +func (k Keeper) IsEnabled(ctx sdk.Context) bool { + return k.GetParams(ctx).Enabled +} + +func (k Keeper) GetGasPrices(ctx sdk.Context) sdk.DecCoins { + return k.GetParams(ctx).GasPrices.Sort() +} + +func (k Keeper) GetBurnTaxRate(ctx sdk.Context) sdk.Dec { + return k.GetParams(ctx).BurnTaxRate +} + +// GetBypassMinFeeMsgTypes gets the tax2gas module's BypassMinFeeMsgTypes. +func (k Keeper) GetBypassMinFeeMsgTypes(ctx sdk.Context) []string { + return k.GetParams(ctx).BypassMinFeeMsgTypes +} + +// GetBypassMinFeeMsgTypes gets the tax2gas module's BypassMinFeeMsgTypes. +func (k Keeper) GetMaxTotalBypassMinFeeMsgGasUsage(ctx sdk.Context) uint64 { + return k.GetParams(ctx).MaxTotalBypassMinFeeMsgGasUsage +} diff --git a/x/tax2gas/keeper/keeper_test.go b/x/tax2gas/keeper/keeper_test.go new file mode 100644 index 000000000..d0694d89e --- /dev/null +++ b/x/tax2gas/keeper/keeper_test.go @@ -0,0 +1,122 @@ +package keeper_test + +import ( + "testing" + + "github.com/classic-terra/core/v3/x/tax2gas/keeper" + "github.com/classic-terra/core/v3/x/tax2gas/types" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/stretchr/testify/suite" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmtime "github.com/cometbft/cometbft/types/time" + "github.com/cosmos/cosmos-sdk/baseapp" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" +) + +type KeeperTestSuite struct { + suite.Suite + + ctx sdk.Context + keeper keeper.Keeper + + queryClient types.QueryClient + msgServer types.MsgServer + + encCfg moduletestutil.TestEncodingConfig +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (suite *KeeperTestSuite) SetupTest() { + key := sdk.NewKVStoreKey(types.StoreKey) + testCtx := testutil.DefaultContextWithDB(suite.T(), key, sdk.NewTransientStoreKey("transient_test")) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: tmtime.Now()}) + encCfg := moduletestutil.MakeTestEncodingConfig() + + // gomock initializations + + suite.ctx = ctx + suite.keeper = keeper.NewKeeper( + encCfg.Codec, + key, + authtypes.NewModuleAddress(govtypes.ModuleName).String(), + ) + suite.keeper.SetParams(suite.ctx, types.DefaultParams()) + + types.RegisterInterfaces(encCfg.InterfaceRegistry) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, encCfg.InterfaceRegistry) + types.RegisterQueryServer(queryHelper, suite.keeper) + queryClient := types.NewQueryClient(queryHelper) + + suite.queryClient = queryClient + suite.msgServer = keeper.NewMsgServerImpl(suite.keeper) + suite.encCfg = encCfg +} + +func (suite *KeeperTestSuite) TestGetAuthority() { + NewKeeperWithAuthority := func(authority string) keeper.Keeper { + return keeper.NewKeeper( + moduletestutil.MakeTestEncodingConfig().Codec, + sdk.NewKVStoreKey(types.StoreKey), + authority, + ) + } + + tests := map[string]string{ + "some random account": "cosmos139f7kncmglres2nf3h4hc4tade85ekfr8sulz5", + "gov module account": authtypes.NewModuleAddress(govtypes.ModuleName).String(), + "another module account": authtypes.NewModuleAddress(minttypes.ModuleName).String(), + } + + for name, expected := range tests { + suite.T().Run(name, func(t *testing.T) { + kpr := NewKeeperWithAuthority(expected) + actual := kpr.GetAuthority() + suite.Require().Equal(expected, actual) + }) + } +} + +func (suite *KeeperTestSuite) TestSetParams() { + ctx, tax2gasKeeper := suite.ctx, suite.keeper + require := suite.Require() + + tax2gasKeeper.SetParams(ctx, types.DefaultParams()) + tests := []struct { + name string + params types.Params + expFail bool + }{ + { + name: "empty gas prices", + params: types.Params{ + Enabled: true, + }, + expFail: true, + }, + { + name: "default params", + params: types.DefaultParams(), + expFail: false, + }, + } + + for _, tc := range tests { + suite.T().Run(tc.name, func(t *testing.T) { + err := tax2gasKeeper.SetParams(ctx, tc.params) + if tc.expFail { + require.Error(err) + } else { + require.NoError(err) + } + }) + } +} diff --git a/x/tax2gas/keeper/msg_server.go b/x/tax2gas/keeper/msg_server.go new file mode 100644 index 000000000..2bfff7a50 --- /dev/null +++ b/x/tax2gas/keeper/msg_server.go @@ -0,0 +1,36 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +type msgServer struct { + Keeper +} + +var _ types.MsgServer = msgServer{} + +// NewMsgServerImpl returns an implementation of the tax2gas MsgServer interface +// for the provided Keeper. +func NewMsgServerImpl(keeper Keeper) types.MsgServer { + return &msgServer{Keeper: keeper} +} + +func (k msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if k.GetAuthority() != req.Authority { + return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.GetAuthority(), req.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := k.SetParams(ctx, req.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} diff --git a/x/tax2gas/keeper/msg_server_test.go b/x/tax2gas/keeper/msg_server_test.go new file mode 100644 index 000000000..6867de094 --- /dev/null +++ b/x/tax2gas/keeper/msg_server_test.go @@ -0,0 +1,60 @@ +package keeper_test + +import ( + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +func (suite *KeeperTestSuite) TestMsgUpdateParams() { + // default params + params := types.DefaultParams() + + testCases := []struct { + name string + input *types.MsgUpdateParams + expErr bool + expErrMsg string + }{ + { + name: "invalid authority", + input: &types.MsgUpdateParams{ + Authority: "invalid", + Params: params, + }, + expErr: true, + expErrMsg: "invalid authority", + }, + { + name: "empty gas prices", + input: &types.MsgUpdateParams{ + Authority: suite.keeper.GetAuthority(), + Params: types.Params{ + Enabled: true, + }, + }, + expErr: true, + expErrMsg: "must provide at least 1 gas prices", + }, + { + name: "all good", + input: &types.MsgUpdateParams{ + Authority: suite.keeper.GetAuthority(), + Params: params, + }, + expErr: false, + }, + } + + for _, tc := range testCases { + tc := tc + suite.Run(tc.name, func() { + _, err := suite.msgServer.UpdateParams(suite.ctx, tc.input) + + if tc.expErr { + suite.Require().Error(err) + suite.Require().Contains(err.Error(), tc.expErrMsg) + } else { + suite.Require().NoError(err) + } + }) + } +} diff --git a/x/tax2gas/keeper/params.go b/x/tax2gas/keeper/params.go new file mode 100644 index 000000000..d36ab16e1 --- /dev/null +++ b/x/tax2gas/keeper/params.go @@ -0,0 +1,34 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +// SetParams sets the tax2gas module's parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error { + if err := params.Validate(); err != nil { + return err + } + store := ctx.KVStore(k.storeKey) + bz, err := k.cdc.Marshal(¶ms) + if err != nil { + return err + } + store.Set(types.ParamsKey, bz) + + return nil +} + +// GetParams gets the tax2gas module's parameters. +func (k Keeper) GetParams(clientCtx sdk.Context) (params types.Params) { + store := clientCtx.KVStore(k.storeKey) + bz := store.Get(types.ParamsKey) + if bz == nil { + return params + } + + k.cdc.MustUnmarshal(bz, ¶ms) + return params +} diff --git a/x/tax2gas/keeper/querier.go b/x/tax2gas/keeper/querier.go new file mode 100644 index 000000000..b475751c2 --- /dev/null +++ b/x/tax2gas/keeper/querier.go @@ -0,0 +1,23 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +var _ types.QueryServer = Keeper{} + +// Params queries params of tax2gas module +func (k Keeper) Params(c context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil +} + +// BurnTaxRate queries burn tax rate of tax2gas module +func (k Keeper) BurnTaxRate(c context.Context, _ *types.QueryBurnTaxRateRequest) (*types.QueryBurnTaxRateResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + return &types.QueryBurnTaxRateResponse{BurnTaxRate: k.GetBurnTaxRate(ctx)}, nil +} diff --git a/x/tax2gas/keeper/querier_test.go b/x/tax2gas/keeper/querier_test.go new file mode 100644 index 000000000..d4d3d04b1 --- /dev/null +++ b/x/tax2gas/keeper/querier_test.go @@ -0,0 +1,14 @@ +package keeper_test + +import ( + "context" + + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +func (suite *KeeperTestSuite) TestQueryParams() { + res, err := suite.queryClient.Params(context.Background(), &types.QueryParamsRequest{}) + suite.Require().NoError(err) + suite.Require().NotNil(res) + suite.Require().Equal(suite.keeper.GetParams(suite.ctx), res.GetParams()) +} diff --git a/x/tax2gas/module.go b/x/tax2gas/module.go new file mode 100644 index 000000000..e34c9764b --- /dev/null +++ b/x/tax2gas/module.go @@ -0,0 +1,122 @@ +package module + +import ( + "context" + "encoding/json" + "fmt" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + "github.com/classic-terra/core/v3/x/tax2gas/client/cli" + "github.com/classic-terra/core/v3/x/tax2gas/keeper" + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +type AppModuleBasic struct { + cdc codec.Codec +} + +func (AppModuleBasic) Name() string { return types.ModuleName } + +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesis()) +} + +// ValidateGenesis performs genesis state validation for the tax2gas module. +func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var genState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + return genState.Validate() +} + +// --------------------------------------- +// Interfaces. +func (b AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil { + panic(err) + } +} + +func (b AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +func (b AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// RegisterInterfaces registers interfaces and implementations of the tax2gas module. +func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(registry) +} + +type AppModule struct { + AppModuleBasic + + k keeper.Keeper +} + +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.k)) + // queryproto.RegisterQueryServer(cfg.QueryServer(), grpc.Querier{Q: module.NewQuerier(am.k)}) + types.RegisterQueryServer(cfg.QueryServer(), am.k) +} + +func NewAppModule(cdc codec.Codec, tax2gasKeeper keeper.Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{cdc}, + k: tax2gasKeeper, + } +} + +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) { +} + +// QuerierRoute returns the tax2gas module's querier route name. +func (AppModule) QuerierRoute() string { return types.RouterKey } + +// InitGenesis performs genesis initialization for the tax2gas module. +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + + cdc.MustUnmarshalJSON(gs, &genesisState) + InitGenesis(ctx, am.k, &genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the tax2gas. +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + genState := am.k.ExportGenesis(ctx) + return cdc.MustMarshalJSON(genState) +} + +// BeginBlock performs TODO. +func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock performs TODO. +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// ConsensusVersion implements AppModule/ConsensusVersion. +func (AppModule) ConsensusVersion() uint64 { return 1 } diff --git a/custom/auth/ante/fee_burntax.go b/x/tax2gas/post/burntax.go similarity index 94% rename from custom/auth/ante/fee_burntax.go rename to x/tax2gas/post/burntax.go index e69f2448b..c59381112 100644 --- a/custom/auth/ante/fee_burntax.go +++ b/x/tax2gas/post/burntax.go @@ -1,4 +1,4 @@ -package ante +package post import ( errorsmod "cosmossdk.io/errors" @@ -12,7 +12,7 @@ import ( ) // BurnTaxSplit splits -func (fd FeeDecorator) BurnTaxSplit(ctx sdk.Context, taxes sdk.Coins) (err error) { +func (fd Tax2gasPostDecorator) BurnTaxSplit(ctx sdk.Context, taxes sdk.Coins) (err error) { burnSplitRate := fd.treasuryKeeper.GetBurnSplitRate(ctx) oracleSplitRate := fd.treasuryKeeper.GetOracleSplitRate(ctx) communityTax := fd.distrKeeper.GetCommunityTax(ctx) @@ -90,5 +90,7 @@ func (fd FeeDecorator) BurnTaxSplit(ctx sdk.Context, taxes sdk.Coins) (err error } } + // Record tax proceeds + fd.treasuryKeeper.RecordEpochTaxProceeds(ctx, taxes) return nil } diff --git a/x/tax2gas/post/post.go b/x/tax2gas/post/post.go new file mode 100644 index 000000000..8435ecfe7 --- /dev/null +++ b/x/tax2gas/post/post.go @@ -0,0 +1,173 @@ +package post + +import ( + sdkmath "cosmossdk.io/math" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + 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" +) + +type Tax2gasPostDecorator struct { + accountKeeper ante.AccountKeeper + bankKeeper types.BankKeeper + feegrantKeeper types.FeegrantKeeper + treasuryKeeper types.TreasuryKeeper + distrKeeper types.DistrKeeper + tax2gasKeeper tax2gasKeeper.Keeper +} + +func NewTax2GasPostDecorator(accountKeeper ante.AccountKeeper, bankKeeper types.BankKeeper, feegrantKeeper types.FeegrantKeeper, treasuryKeeper types.TreasuryKeeper, distrKeeper types.DistrKeeper, tax2gasKeeper tax2gasKeeper.Keeper) Tax2gasPostDecorator { + return Tax2gasPostDecorator{ + accountKeeper: accountKeeper, + bankKeeper: bankKeeper, + feegrantKeeper: feegrantKeeper, + treasuryKeeper: treasuryKeeper, + distrKeeper: distrKeeper, + tax2gasKeeper: tax2gasKeeper, + } +} + +func (tgd Tax2gasPostDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, success bool, next sdk.PostHandler) (sdk.Context, error) { + feeTx, ok := tx.(sdk.FeeTx) + if !ok { + return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") + } + + if !simulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 { + return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas") + } + msgs := feeTx.GetMsgs() + if tax2gasutils.IsOracleTx(msgs) || !tgd.tax2gasKeeper.IsEnabled(ctx) { + return next(ctx, tx, simulate, success) + } + + feeCoins := feeTx.GetFee() + anteConsumedGas, ok := ctx.Value(types.AnteConsumedGas).(uint64) + if !simulate && !ok { + // If cannot found the anteConsumedGas, that's mean the tx is bypass + // Skip this tx as it's bypass + return next(ctx, tx, simulate, success) + } + + // Get paid denom identified in ante handler + paidDenom, ok := ctx.Value(types.PaidDenom).(string) + if !simulate && !ok { + // If cannot found the paidDenom, that's mean this is the init genesis tx + // Skip this tx as it's init genesis tx + return next(ctx, tx, simulate, success) + } + + gasPrices, ok := ctx.Value(types.FinalGasPrices).(sdk.DecCoins) + if !ok { + gasPrices = tgd.tax2gasKeeper.GetGasPrices(ctx) + } + + found, paidDenomGasPrice := tax2gasutils.GetGasPriceByDenom(gasPrices, paidDenom) + if !simulate && !found { + return ctx, types.ErrDenomNotFound + } + paidAmount := paidDenomGasPrice.Mul(sdk.NewDec(int64(anteConsumedGas))) + + if !simulate { + // Deduct feeCoins with paid amount + feeCoins = feeCoins.Sub(sdk.NewCoin(paidDenom, paidAmount.Ceil().RoundInt())) + } + + taxGas := ctx.TaxGasMeter().GasConsumed() + + // we consume the gas here as we need to calculate the tax for consumed gas + // if the gas overflow, then that means the tx can't be estimates as normal way + // we need to add the --fee flag manually + totalGasConsumed := ctx.GasMeter().GasConsumed() + + if taxGas.IsUint64() { + taxGasUint64 := taxGas.Uint64() + // Check if gas not overflow + if totalGasConsumed+taxGasUint64 >= totalGasConsumed && totalGasConsumed+taxGasUint64 >= taxGasUint64 { + if simulate { + ctx.GasMeter().ConsumeGas(taxGasUint64, "consume tax gas") + } + } + } + + // Deduct the gas consumed amount spent on ante handler + totalGasRemaining := sdkmath.NewInt(int64(totalGasConsumed - anteConsumedGas)).Add(taxGas) + + feePayer := feeTx.FeePayer() + feeGranter := feeTx.FeeGranter() + + // if feegranter set deduct fee from feegranter account. + // this works with only when feegrant enabled. + if feeGranter != nil { + allowance, err := tgd.feegrantKeeper.GetAllowance(ctx, feeGranter, feePayer) + if err != nil { + return ctx, errorsmod.Wrapf(err, "fee-grant not found with granter %s and grantee %s", feeGranter, feePayer) + } + + gasRemainingFees, err := tax2gasutils.ComputeFeesOnGasConsumed(tx, gasPrices, totalGasRemaining) + if err != nil { + return ctx, err + } + + // For this tx, we only accept to pay by one denom + for _, feeRequired := range gasRemainingFees { + _, err := allowance.Accept(ctx, sdk.NewCoins(feeRequired), feeTx.GetMsgs()) + if err == nil { + err = tgd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, sdk.NewCoins(feeRequired), feeTx.GetMsgs()) + if err != nil { + return ctx, errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) + } + feeGranter := tgd.accountKeeper.GetAccount(ctx, feeGranter) + err = tgd.bankKeeper.SendCoinsFromAccountToModule(ctx, feeGranter.GetAddress(), authtypes.FeeCollectorName, sdk.NewCoins(feeRequired)) + if err != nil { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + } + + // Calculate tax fee and BurnTaxSplit + _, gasPrice := tax2gasutils.GetGasPriceByDenom(gasPrices, feeRequired.Denom) + taxFee := gasPrice.MulInt(taxGas).Ceil().RoundInt() + + err := tgd.BurnTaxSplit(ctx, sdk.NewCoins(sdk.NewCoin(feeRequired.Denom, taxFee))) + if err != nil { + return ctx, err + } + return next(ctx, tx, simulate, success) + } + } + return ctx, errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer) + } + + // First, we will deduct the fees covered taxGas and handle BurnTaxSplit + taxes, payableFees, gasRemaining := tax2gasutils.CalculateTaxesAndPayableFee(gasPrices, feeCoins, taxGas, totalGasRemaining) + if !simulate && !ctx.IsCheckTx() && gasRemaining.IsPositive() { + gasRemainingFees, err := tax2gasutils.ComputeFeesOnGasConsumed(tx, gasPrices, gasRemaining) + if err != nil { + return ctx, err + } + + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "fees are not enough to pay for gas, need to cover %s gas more, which equal to %q ", gasRemaining.String(), gasRemainingFees) + } + feePayerAccount := tgd.accountKeeper.GetAccount(ctx, feePayer) + + if !simulate && taxes.IsZero() { + payableFees = feeCoins + } + err := tgd.bankKeeper.SendCoinsFromAccountToModule(ctx, feePayerAccount.GetAddress(), authtypes.FeeCollectorName, payableFees) + if err != nil { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error()) + } + + err = tgd.BurnTaxSplit(ctx, taxes) + if err != nil { + return ctx, err + } + + return next(ctx, tx, simulate, success) +} diff --git a/x/tax2gas/types/codec.go b/x/tax2gas/types/codec.go new file mode 100644 index 000000000..cffb8f985 --- /dev/null +++ b/x/tax2gas/types/codec.go @@ -0,0 +1,39 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" +) + +// RegisterLegacyAminoCodec registers the necessary x/tax2gas interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgUpdateParams{}, "terra/tax2gas/MsgUpdateParams", nil) +} + +func RegisterInterfaces(registry types.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgUpdateParams{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + sdk.RegisterLegacyAminoCodec(amino) + RegisterLegacyAminoCodec(authzcodec.Amino) + + amino.Seal() +} diff --git a/x/tax2gas/types/errors.go b/x/tax2gas/types/errors.go new file mode 100644 index 000000000..501df004d --- /dev/null +++ b/x/tax2gas/types/errors.go @@ -0,0 +1,12 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" +) + +// Tax2Gas errors +var ( + 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/events.go b/x/tax2gas/types/events.go new file mode 100644 index 000000000..885a885a7 --- /dev/null +++ b/x/tax2gas/types/events.go @@ -0,0 +1,5 @@ +package types + +const ( + AttributeValueCategory = ModuleName +) diff --git a/x/tax2gas/types/expected_keeper.go b/x/tax2gas/types/expected_keeper.go new file mode 100644 index 000000000..2b6e805c6 --- /dev/null +++ b/x/tax2gas/types/expected_keeper.go @@ -0,0 +1,42 @@ +package types + +import ( + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" +) + +// 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 + GetOracleSplitRate(ctx sdk.Context) sdk.Dec +} + +// 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 +} + +type DistrKeeper interface { + FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error + GetFeePool(ctx sdk.Context) distributiontypes.FeePool + GetCommunityTax(ctx sdk.Context) math.LegacyDec + SetFeePool(ctx sdk.Context, feePool distributiontypes.FeePool) +} diff --git a/x/tax2gas/types/genesis.go b/x/tax2gas/types/genesis.go new file mode 100644 index 000000000..964a1a3ea --- /dev/null +++ b/x/tax2gas/types/genesis.go @@ -0,0 +1,17 @@ +package types + +// DefaultGenesis returns the default tax2gas genesis state. +func DefaultGenesis() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + if err := gs.Params.Validate(); err != nil { + return err + } + return nil +} diff --git a/x/tax2gas/types/genesis.pb.go b/x/tax2gas/types/genesis.pb.go new file mode 100644 index 000000000..0dedecbb5 --- /dev/null +++ b/x/tax2gas/types/genesis.pb.go @@ -0,0 +1,710 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: terra/tax2gas/v1beta1/genesis.proto + +package types + +import ( + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Params struct { + GasPrices github_com_cosmos_cosmos_sdk_types.DecCoins `protobuf:"bytes,1,rep,name=gas_prices,json=gasPrices,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.DecCoins" json:"gas_prices" yaml:"gas_prices"` + BurnTaxRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,2,opt,name=burn_tax_rate,json=burnTaxRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"burn_tax_rate"` + Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"` + // bypass_min_fee_msg_types defines a list of message type urls + // that are free of fee charge. + BypassMinFeeMsgTypes []string `protobuf:"bytes,4,rep,name=bypass_min_fee_msg_types,json=bypassMinFeeMsgTypes,proto3" json:"bypass_min_fee_msg_types,omitempty" yaml:"bypass_min_fee_msg_types"` + // max_total_bypass_min_fee_msg_gas_usage defines the total maximum gas usage + // allowed for a transaction containing only messages of types in bypass_min_fee_msg_types + // to bypass fee charge. + MaxTotalBypassMinFeeMsgGasUsage uint64 `protobuf:"varint,5,opt,name=max_total_bypass_min_fee_msg_gas_usage,json=maxTotalBypassMinFeeMsgGasUsage,proto3" json:"max_total_bypass_min_fee_msg_gas_usage,omitempty"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_589c4ef0e5113034, []int{0} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetGasPrices() github_com_cosmos_cosmos_sdk_types.DecCoins { + if m != nil { + return m.GasPrices + } + return nil +} + +func (m *Params) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *Params) GetBypassMinFeeMsgTypes() []string { + if m != nil { + return m.BypassMinFeeMsgTypes + } + return nil +} + +func (m *Params) GetMaxTotalBypassMinFeeMsgGasUsage() uint64 { + if m != nil { + return m.MaxTotalBypassMinFeeMsgGasUsage + } + return 0 +} + +// GenesisState defines the tax2gas module's genesis state. +type GenesisState struct { + // params is the container of tax2gas parameters. + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_589c4ef0e5113034, []int{1} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*Params)(nil), "terra.tax2gas.v1beta1.Params") + proto.RegisterType((*GenesisState)(nil), "terra.tax2gas.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("terra/tax2gas/v1beta1/genesis.proto", fileDescriptor_589c4ef0e5113034) +} + +var fileDescriptor_589c4ef0e5113034 = []byte{ + // 512 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xbf, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0x73, 0xa4, 0x04, 0xe2, 0xc0, 0x50, 0xab, 0x20, 0xab, 0x02, 0xdb, 0x32, 0x52, 0x65, + 0x15, 0x6a, 0xd3, 0x74, 0x2b, 0x9b, 0x41, 0x54, 0x80, 0x22, 0x2a, 0x13, 0x16, 0x16, 0xeb, 0xd9, + 0x3d, 0x8c, 0x45, 0xce, 0x67, 0xf9, 0x5d, 0x2a, 0x67, 0x64, 0xed, 0xc4, 0xc8, 0xc8, 0x88, 0x98, + 0xfa, 0x67, 0x74, 0xec, 0x88, 0x18, 0x02, 0x4a, 0x86, 0x4a, 0x8c, 0xdd, 0xd8, 0xd0, 0xf9, 0x9c, + 0x52, 0xa4, 0x56, 0x62, 0xf1, 0x8f, 0x7b, 0x9f, 0xef, 0xf7, 0x9e, 0xde, 0xfb, 0x6a, 0xf7, 0x04, + 0x2d, 0x4b, 0xf0, 0x05, 0x54, 0xfd, 0x14, 0xd0, 0xdf, 0xdf, 0x8c, 0xa9, 0x80, 0x4d, 0x3f, 0xa5, + 0x39, 0xc5, 0x0c, 0xbd, 0xa2, 0xe4, 0x82, 0xeb, 0xb7, 0x6a, 0xc8, 0x6b, 0x20, 0xaf, 0x81, 0x56, + 0x57, 0x52, 0x9e, 0xf2, 0x9a, 0xf0, 0xe5, 0x97, 0x82, 0x57, 0xcd, 0x84, 0x23, 0xe3, 0xe8, 0xc7, + 0x80, 0xf4, 0xcc, 0x2f, 0xe1, 0x59, 0xde, 0xd4, 0x97, 0x81, 0x65, 0x39, 0xf7, 0xeb, 0xa7, 0x3a, + 0x72, 0x7e, 0xb7, 0xb5, 0xce, 0x2e, 0x94, 0xc0, 0x50, 0x3f, 0x20, 0x9a, 0x96, 0x02, 0x46, 0x45, + 0x99, 0x25, 0x14, 0x0d, 0x62, 0xb7, 0xdd, 0x5e, 0xff, 0x8e, 0xa7, 0x3c, 0x3d, 0xe9, 0xb9, 0xb8, + 0xde, 0x7b, 0x42, 0x93, 0xc7, 0x3c, 0xcb, 0x83, 0xc1, 0xd1, 0xd4, 0x6a, 0x9d, 0x4e, 0xad, 0xe5, + 0x09, 0xb0, 0xd1, 0xb6, 0xf3, 0x57, 0xed, 0x7c, 0xfd, 0x61, 0xdd, 0x4f, 0x33, 0xf1, 0x6e, 0x1c, + 0x7b, 0x09, 0x67, 0x7e, 0xd3, 0x98, 0x7a, 0x6d, 0xe0, 0xde, 0x7b, 0x5f, 0x4c, 0x0a, 0x8a, 0x0b, + 0x23, 0xfc, 0x72, 0x72, 0xb8, 0x4e, 0xc2, 0x6e, 0x0a, 0xb8, 0x5b, 0xeb, 0xf5, 0x50, 0xbb, 0x19, + 0x8f, 0xcb, 0x3c, 0x12, 0x50, 0x45, 0x25, 0x08, 0x6a, 0x5c, 0xb1, 0x89, 0xdb, 0x0d, 0x3c, 0x79, + 0xe1, 0xf7, 0xa9, 0xb5, 0xf6, 0x7f, 0xde, 0x61, 0x4f, 0x9a, 0x0c, 0xa1, 0x0a, 0x41, 0x50, 0xdd, + 0xd0, 0xae, 0xd1, 0x1c, 0xe2, 0x11, 0xdd, 0x33, 0xda, 0x36, 0x71, 0xaf, 0x87, 0x8b, 0x5f, 0xfd, + 0x03, 0xd1, 0x8c, 0x78, 0x52, 0x00, 0x62, 0xc4, 0xb2, 0x3c, 0x7a, 0x4b, 0x69, 0xc4, 0x30, 0x8d, + 0x6a, 0x1f, 0x63, 0xc9, 0x6e, 0xbb, 0xdd, 0xe0, 0xd9, 0xaf, 0xa9, 0xe5, 0x5c, 0xc6, 0x3c, 0xe0, + 0x2c, 0x13, 0x94, 0x15, 0x62, 0x72, 0x3a, 0xb5, 0x2c, 0x35, 0x8c, 0xcb, 0x58, 0x27, 0x5c, 0x51, + 0xa5, 0x41, 0x96, 0x3f, 0xa5, 0x74, 0x80, 0xe9, 0x50, 0x1e, 0xeb, 0x2f, 0xb5, 0x35, 0x06, 0x55, + 0x24, 0xb8, 0x80, 0x51, 0x74, 0x81, 0x58, 0x0e, 0x77, 0x8c, 0x90, 0x52, 0xe3, 0xaa, 0x4d, 0xdc, + 0xa5, 0xd0, 0x62, 0x50, 0x0d, 0x25, 0x1c, 0xfc, 0xeb, 0xb6, 0x03, 0xf8, 0x5a, 0x62, 0xdb, 0xd6, + 0xa7, 0xcf, 0x16, 0x39, 0x38, 0x39, 0x5c, 0xbf, 0xad, 0x82, 0x56, 0x9d, 0x45, 0x4d, 0x2d, 0xdc, + 0x79, 0xa1, 0xdd, 0xd8, 0x51, 0x61, 0x7b, 0x25, 0xe4, 0x7c, 0x1e, 0x69, 0x9d, 0xa2, 0xae, 0x18, + 0xc4, 0x26, 0x6e, 0xaf, 0x7f, 0xd7, 0xbb, 0x30, 0x7c, 0x9e, 0x92, 0x07, 0x4b, 0x72, 0x17, 0x61, + 0x23, 0x09, 0x9e, 0x1f, 0xcd, 0x4c, 0x72, 0x3c, 0x33, 0xc9, 0xcf, 0x99, 0x49, 0x3e, 0xce, 0xcd, + 0xd6, 0xf1, 0xdc, 0x6c, 0x7d, 0x9b, 0x9b, 0xad, 0x37, 0x0f, 0xcf, 0xef, 0x6a, 0x04, 0x88, 0x59, + 0xb2, 0xa1, 0x3a, 0x4a, 0x78, 0x49, 0xfd, 0xfd, 0xad, 0x73, 0x9d, 0xd5, 0x13, 0x8a, 0x3b, 0x75, + 0x36, 0xb7, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xa0, 0xf6, 0x80, 0x22, 0x03, 0x00, 0x00, +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MaxTotalBypassMinFeeMsgGasUsage != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.MaxTotalBypassMinFeeMsgGasUsage)) + i-- + dAtA[i] = 0x28 + } + if len(m.BypassMinFeeMsgTypes) > 0 { + for iNdEx := len(m.BypassMinFeeMsgTypes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.BypassMinFeeMsgTypes[iNdEx]) + copy(dAtA[i:], m.BypassMinFeeMsgTypes[iNdEx]) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.BypassMinFeeMsgTypes[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if m.Enabled { + i-- + if m.Enabled { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + { + size := m.BurnTaxRate.Size() + i -= size + if _, err := m.BurnTaxRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.GasPrices) > 0 { + for iNdEx := len(m.GasPrices) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.GasPrices[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.GasPrices) > 0 { + for _, e := range m.GasPrices { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + l = m.BurnTaxRate.Size() + n += 1 + l + sovGenesis(uint64(l)) + if m.Enabled { + n += 2 + } + if len(m.BypassMinFeeMsgTypes) > 0 { + for _, s := range m.BypassMinFeeMsgTypes { + l = len(s) + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.MaxTotalBypassMinFeeMsgGasUsage != 0 { + n += 1 + sovGenesis(uint64(m.MaxTotalBypassMinFeeMsgGasUsage)) + } + return n +} + +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GasPrices", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GasPrices = append(m.GasPrices, types.DecCoin{}) + if err := m.GasPrices[len(m.GasPrices)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BurnTaxRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BurnTaxRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Enabled = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BypassMinFeeMsgTypes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BypassMinFeeMsgTypes = append(m.BypassMinFeeMsgTypes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxTotalBypassMinFeeMsgGasUsage", wireType) + } + m.MaxTotalBypassMinFeeMsgGasUsage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxTotalBypassMinFeeMsgGasUsage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/tax2gas/types/keys.go b/x/tax2gas/types/keys.go new file mode 100644 index 000000000..ef679c3a2 --- /dev/null +++ b/x/tax2gas/types/keys.go @@ -0,0 +1,22 @@ +package types + +const ( + ModuleName = "tax2gas" + + StoreKey = ModuleName + + RouterKey = ModuleName + + AnteConsumedGas = "anteConsumedGas" + + TaxGas = "taxGas" + + FinalGasPrices = "finalGasPrices" + + PaidDenom = "paidDenom" +) + +// Key defines the store key for tax2gas. +var ( + ParamsKey = []byte{0x1} +) diff --git a/x/tax2gas/types/msgs.go b/x/tax2gas/types/msgs.go new file mode 100644 index 000000000..b0b3e5b48 --- /dev/null +++ b/x/tax2gas/types/msgs.go @@ -0,0 +1,26 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + TypeMsgUpdateParams = "update_params" +) + +var _ sdk.Msg = &MsgUpdateParams{} + +func (msg MsgUpdateParams) Route() string { return ModuleName } +func (msg MsgUpdateParams) Type() string { return TypeMsgUpdateParams } +func (msg MsgUpdateParams) ValidateBasic() error { + return msg.Params.Validate() +} + +func (msg MsgUpdateParams) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +func (msg MsgUpdateParams) GetSigners() []sdk.AccAddress { + authority, _ := sdk.AccAddressFromBech32(msg.Authority) + return []sdk.AccAddress{authority} +} diff --git a/x/tax2gas/types/params.go b/x/tax2gas/types/params.go new file mode 100644 index 000000000..f152d000a --- /dev/null +++ b/x/tax2gas/types/params.go @@ -0,0 +1,79 @@ +package types + +import ( + fmt "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + + ibcclienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + ibcchanneltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" +) + +var ( + // DefaultMinGasPrices is set at runtime to the staking token with zero amount i.e. "0uatom" + // see DefaultZeroGlobalFee method in gaia/x/globalfee/ante/fee.go. + DefaultGasPrices = sdk.NewDecCoins( + sdk.NewDecCoinFromDec("uluna", sdk.NewDecWithPrec(28325, 3)), + sdk.NewDecCoinFromDec("usdr", sdk.NewDecWithPrec(52469, 5)), + sdk.NewDecCoinFromDec("uusd", sdk.NewDecWithPrec(75, 2)), + sdk.NewDecCoinFromDec("ukrw", sdk.NewDecWithPrec(850, 0)), + sdk.NewDecCoinFromDec("umnt", sdk.NewDecWithPrec(2142855, 3)), + sdk.NewDecCoinFromDec("ueur", sdk.NewDecWithPrec(625, 3)), + sdk.NewDecCoinFromDec("ucny", sdk.NewDecWithPrec(49, 1)), + sdk.NewDecCoinFromDec("ujpy", sdk.NewDecWithPrec(8185, 2)), + sdk.NewDecCoinFromDec("ugbp", sdk.NewDecWithPrec(55, 2)), + sdk.NewDecCoinFromDec("uinr", sdk.NewDecWithPrec(544, 1)), + sdk.NewDecCoinFromDec("ucad", sdk.NewDecWithPrec(95, 2)), + sdk.NewDecCoinFromDec("uchf", sdk.NewDecWithPrec(7, 1)), + sdk.NewDecCoinFromDec("uaud", sdk.NewDecWithPrec(95, 2)), + sdk.NewDecCoinFromDec("usgd", sdk.NewDec(1)), + sdk.NewDecCoinFromDec("uthb", sdk.NewDecWithPrec(231, 1)), + sdk.NewDecCoinFromDec("usek", sdk.NewDecWithPrec(625, 2)), + sdk.NewDecCoinFromDec("unok", sdk.NewDecWithPrec(625, 2)), + sdk.NewDecCoinFromDec("udkk", sdk.NewDecWithPrec(45, 1)), + sdk.NewDecCoinFromDec("uidr", sdk.NewDecWithPrec(10900, 0)), + sdk.NewDecCoinFromDec("uphp", sdk.NewDecWithPrec(38, 0)), + sdk.NewDecCoinFromDec("uhkd", sdk.NewDecWithPrec(585, 2)), + sdk.NewDecCoinFromDec("umyr", sdk.NewDecWithPrec(3, 0)), + sdk.NewDecCoinFromDec("utwd", sdk.NewDecWithPrec(20, 0)), + ) + DefaultBypassMinFeeMsgTypes = []string{ + sdk.MsgTypeURL(&ibcclienttypes.MsgUpdateClient{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgRecvPacket{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgAcknowledgement{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeout{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeoutOnClose{}), + } + + // maxTotalBypassMinFeeMsgGasUsage is the allowed maximum gas usage + // for all the bypass msgs in a transactions. + // A transaction that contains only bypass message types and the gas usage does not + // exceed maxTotalBypassMinFeeMsgGasUsage can be accepted with a zero fee. + // For details, see gaiafeeante.NewFeeDecorator() + DefaultmaxTotalBypassMinFeeMsgGasUsage uint64 = 1_000_000 +) + +func NewParams() Params { + return Params{} +} + +// DefaultParams are the default tax2gas module parameters. +func DefaultParams() Params { + return Params{ + GasPrices: DefaultGasPrices, + BurnTaxRate: sdk.NewDecWithPrec(5, 3), + Enabled: true, + BypassMinFeeMsgTypes: DefaultBypassMinFeeMsgTypes, + MaxTotalBypassMinFeeMsgGasUsage: DefaultmaxTotalBypassMinFeeMsgGasUsage, + } +} + +// Validate validates params. +func (p Params) Validate() error { + if p.Enabled { + if len(p.GasPrices) == 0 { + return fmt.Errorf("must provide at least 1 gas prices") + } + } + return nil +} diff --git a/x/tax2gas/types/query.pb.go b/x/tax2gas/types/query.pb.go new file mode 100644 index 000000000..714b6a802 --- /dev/null +++ b/x/tax2gas/types/query.pb.go @@ -0,0 +1,860 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: terra/tax2gas/v1beta1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// =============================== Params +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bfce3f3a760d419d, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +type QueryParamsResponse struct { + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bfce3f3a760d419d, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +type QueryBurnTaxRateRequest struct { +} + +func (m *QueryBurnTaxRateRequest) Reset() { *m = QueryBurnTaxRateRequest{} } +func (m *QueryBurnTaxRateRequest) String() string { return proto.CompactTextString(m) } +func (*QueryBurnTaxRateRequest) ProtoMessage() {} +func (*QueryBurnTaxRateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bfce3f3a760d419d, []int{2} +} +func (m *QueryBurnTaxRateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBurnTaxRateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBurnTaxRateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryBurnTaxRateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBurnTaxRateRequest.Merge(m, src) +} +func (m *QueryBurnTaxRateRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryBurnTaxRateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBurnTaxRateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBurnTaxRateRequest proto.InternalMessageInfo + +type QueryBurnTaxRateResponse struct { + BurnTaxRate github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,1,opt,name=burn_tax_rate,json=burnTaxRate,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"burn_tax_rate"` +} + +func (m *QueryBurnTaxRateResponse) Reset() { *m = QueryBurnTaxRateResponse{} } +func (m *QueryBurnTaxRateResponse) String() string { return proto.CompactTextString(m) } +func (*QueryBurnTaxRateResponse) ProtoMessage() {} +func (*QueryBurnTaxRateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bfce3f3a760d419d, []int{3} +} +func (m *QueryBurnTaxRateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryBurnTaxRateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryBurnTaxRateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryBurnTaxRateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryBurnTaxRateResponse.Merge(m, src) +} +func (m *QueryBurnTaxRateResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryBurnTaxRateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryBurnTaxRateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryBurnTaxRateResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "terra.tax2gas.v1beta1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "terra.tax2gas.v1beta1.QueryParamsResponse") + proto.RegisterType((*QueryBurnTaxRateRequest)(nil), "terra.tax2gas.v1beta1.QueryBurnTaxRateRequest") + proto.RegisterType((*QueryBurnTaxRateResponse)(nil), "terra.tax2gas.v1beta1.QueryBurnTaxRateResponse") +} + +func init() { proto.RegisterFile("terra/tax2gas/v1beta1/query.proto", fileDescriptor_bfce3f3a760d419d) } + +var fileDescriptor_bfce3f3a760d419d = []byte{ + // 400 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x6f, 0xda, 0x30, + 0x1c, 0xc5, 0x13, 0xb4, 0x21, 0xcd, 0x68, 0x17, 0x0f, 0x04, 0x8b, 0x20, 0x6c, 0x99, 0x34, 0x6d, + 0x93, 0xb0, 0x07, 0x1c, 0x77, 0x8b, 0x76, 0xda, 0x69, 0x8b, 0x76, 0xda, 0x05, 0x39, 0x99, 0x95, + 0x45, 0x03, 0x3b, 0xd8, 0x0e, 0x0a, 0x97, 0x1e, 0xfa, 0x09, 0xaa, 0xf6, 0xda, 0x0f, 0xc4, 0x11, + 0xa9, 0x97, 0xaa, 0x07, 0x54, 0x41, 0x3f, 0x48, 0x85, 0xe3, 0xb6, 0x50, 0x68, 0xc5, 0x29, 0x91, + 0xfd, 0xfb, 0xbf, 0xf7, 0xfe, 0x2f, 0x01, 0xef, 0x15, 0x15, 0x82, 0x60, 0x45, 0xf2, 0x5e, 0x4c, + 0x24, 0x9e, 0x74, 0x43, 0xaa, 0x48, 0x17, 0x8f, 0x33, 0x2a, 0xa6, 0x28, 0x15, 0x5c, 0x71, 0x58, + 0xd3, 0x08, 0x32, 0x08, 0x32, 0x88, 0x53, 0x8d, 0x79, 0xcc, 0x35, 0x81, 0xd7, 0x6f, 0x05, 0xec, + 0x34, 0x63, 0xce, 0xe3, 0x21, 0xc5, 0x24, 0x4d, 0x30, 0x61, 0x8c, 0x2b, 0xa2, 0x12, 0xce, 0xa4, + 0xb9, 0xfd, 0xb0, 0xdf, 0x2d, 0xa6, 0x8c, 0xca, 0xc4, 0x40, 0x5e, 0x15, 0xc0, 0x5f, 0x6b, 0xfb, + 0x9f, 0x44, 0x90, 0x91, 0x0c, 0xe8, 0x38, 0xa3, 0x52, 0x79, 0x01, 0x78, 0xb3, 0x75, 0x2a, 0x53, + 0xce, 0x24, 0x85, 0xdf, 0x40, 0x39, 0xd5, 0x27, 0x0d, 0xfb, 0x9d, 0xfd, 0xa9, 0xd2, 0x6b, 0xa1, + 0xbd, 0x69, 0x51, 0x31, 0xe6, 0xbf, 0x98, 0x2d, 0xda, 0x56, 0x60, 0x46, 0xbc, 0xb7, 0xa0, 0xae, + 0x35, 0xfd, 0x4c, 0xb0, 0xdf, 0x24, 0x0f, 0x88, 0xa2, 0x77, 0x76, 0x0c, 0x34, 0x76, 0xaf, 0x8c, + 0x67, 0x00, 0x5e, 0x87, 0x99, 0x60, 0x03, 0x45, 0xf2, 0x81, 0x20, 0x8a, 0x6a, 0xeb, 0x57, 0x3e, + 0x5a, 0x6b, 0x5f, 0x2d, 0xda, 0x1f, 0xe3, 0x44, 0xfd, 0xcb, 0x42, 0x14, 0xf1, 0x11, 0x8e, 0xb8, + 0x1c, 0x71, 0x69, 0x1e, 0x1d, 0xf9, 0xf7, 0x3f, 0x56, 0xd3, 0x94, 0x4a, 0xf4, 0x9d, 0x46, 0x41, + 0x25, 0x7c, 0xd0, 0xee, 0x9d, 0x97, 0xc0, 0x4b, 0x6d, 0x08, 0x8f, 0x40, 0xb9, 0x08, 0x0b, 0x3f, + 0x3f, 0xb1, 0xcb, 0x6e, 0x3b, 0xce, 0x97, 0x43, 0xd0, 0x22, 0xbe, 0xd7, 0x3a, 0xbe, 0xb8, 0x39, + 0x2b, 0xd5, 0x61, 0x0d, 0x6f, 0x7f, 0x0d, 0xe3, 0x7a, 0x6a, 0x83, 0xca, 0xc6, 0xd6, 0x10, 0x3d, + 0x27, 0xbd, 0xdb, 0x9c, 0x83, 0x0f, 0xe6, 0x4d, 0x1e, 0x4f, 0xe7, 0x69, 0x42, 0xe7, 0x51, 0x9e, + 0x0d, 0xd6, 0xff, 0x31, 0x5b, 0xba, 0xf6, 0x7c, 0xe9, 0xda, 0xd7, 0x4b, 0xd7, 0x3e, 0x59, 0xb9, + 0xd6, 0x7c, 0xe5, 0x5a, 0x97, 0x2b, 0xd7, 0xfa, 0xf3, 0x75, 0xb3, 0xed, 0x21, 0x91, 0x32, 0x89, + 0x3a, 0x85, 0x4e, 0xc4, 0x05, 0xc5, 0x93, 0x3e, 0xce, 0xef, 0x15, 0x75, 0xf7, 0x61, 0x59, 0xff, + 0x66, 0xfd, 0xdb, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xec, 0x37, 0x77, 0xfb, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + BurnTaxRate(ctx context.Context, in *QueryBurnTaxRateRequest, opts ...grpc.CallOption) (*QueryBurnTaxRateResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/terra.tax2gas.v1beta1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) BurnTaxRate(ctx context.Context, in *QueryBurnTaxRateRequest, opts ...grpc.CallOption) (*QueryBurnTaxRateResponse, error) { + out := new(QueryBurnTaxRateResponse) + err := c.cc.Invoke(ctx, "/terra.tax2gas.v1beta1.Query/BurnTaxRate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + BurnTaxRate(context.Context, *QueryBurnTaxRateRequest) (*QueryBurnTaxRateResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} +func (*UnimplementedQueryServer) BurnTaxRate(ctx context.Context, req *QueryBurnTaxRateRequest) (*QueryBurnTaxRateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BurnTaxRate not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/terra.tax2gas.v1beta1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_BurnTaxRate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryBurnTaxRateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).BurnTaxRate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/terra.tax2gas.v1beta1.Query/BurnTaxRate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).BurnTaxRate(ctx, req.(*QueryBurnTaxRateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "terra.tax2gas.v1beta1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + { + MethodName: "BurnTaxRate", + Handler: _Query_BurnTaxRate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "terra/tax2gas/v1beta1/query.proto", +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *QueryBurnTaxRateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryBurnTaxRateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBurnTaxRateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryBurnTaxRateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryBurnTaxRateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryBurnTaxRateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.BurnTaxRate.Size() + i -= size + if _, err := m.BurnTaxRate.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryBurnTaxRateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryBurnTaxRateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.BurnTaxRate.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryBurnTaxRateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryBurnTaxRateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBurnTaxRateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryBurnTaxRateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryBurnTaxRateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryBurnTaxRateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BurnTaxRate", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BurnTaxRate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/tax2gas/types/query.pb.gw.go b/x/tax2gas/types/query.pb.gw.go new file mode 100644 index 000000000..67a40a180 --- /dev/null +++ b/x/tax2gas/types/query.pb.gw.go @@ -0,0 +1,218 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: terra/tax2gas/v1beta1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_BurnTaxRate_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBurnTaxRateRequest + var metadata runtime.ServerMetadata + + msg, err := client.BurnTaxRate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_BurnTaxRate_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryBurnTaxRateRequest + var metadata runtime.ServerMetadata + + msg, err := server.BurnTaxRate(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BurnTaxRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_BurnTaxRate_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_BurnTaxRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_BurnTaxRate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_BurnTaxRate_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_BurnTaxRate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"terra", "tax2gas", "Params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_BurnTaxRate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"terra", "tax2gas", "BurnTaxRate"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_BurnTaxRate_0 = runtime.ForwardResponseMessage +) diff --git a/x/tax2gas/types/tx.pb.go b/x/tax2gas/types/tx.pb.go new file mode 100644 index 000000000..14e51e963 --- /dev/null +++ b/x/tax2gas/types/tx.pb.go @@ -0,0 +1,599 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: terra/tax2gas/v1beta1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParams struct { + // authority is the address that controls the module (defaults to x/gov unless overwritten). + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the x/tax2gas parameters to update. + // + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_a20cba19b3d258de, []int{0} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// MsgUpdateParamsResponse defines the response structure for executing a +// MsgUpdateParams message. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_a20cba19b3d258de, []int{1} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgUpdateParams)(nil), "terra.tax2gas.v1beta1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "terra.tax2gas.v1beta1.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("terra/tax2gas/v1beta1/tx.proto", fileDescriptor_a20cba19b3d258de) } + +var fileDescriptor_a20cba19b3d258de = []byte{ + // 365 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2b, 0x49, 0x2d, 0x2a, + 0x4a, 0xd4, 0x2f, 0x49, 0xac, 0x30, 0x4a, 0x4f, 0x2c, 0xd6, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, + 0x34, 0xd4, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x05, 0xcb, 0xeb, 0x41, + 0xe5, 0xf5, 0xa0, 0xf2, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0x15, 0xfa, 0x20, 0x16, 0x44, + 0xb1, 0x94, 0x78, 0x72, 0x7e, 0x71, 0x6e, 0x7e, 0xb1, 0x7e, 0x6e, 0x71, 0xba, 0x7e, 0x99, 0x21, + 0x88, 0x82, 0x4a, 0x08, 0x26, 0xe6, 0x66, 0xe6, 0xe5, 0xeb, 0x83, 0x49, 0xa8, 0x90, 0x24, 0x44, + 0x6d, 0x3c, 0xc4, 0x10, 0x08, 0x07, 0x2a, 0xa5, 0x8c, 0xdd, 0x4d, 0xe9, 0xa9, 0x79, 0xa9, 0xc5, + 0x99, 0x50, 0x45, 0x4a, 0xfb, 0x19, 0xb9, 0xf8, 0x7d, 0x8b, 0xd3, 0x43, 0x0b, 0x52, 0x12, 0x4b, + 0x52, 0x03, 0x12, 0x8b, 0x12, 0x73, 0x8b, 0x85, 0xcc, 0xb8, 0x38, 0x13, 0x4b, 0x4b, 0x32, 0xf2, + 0x8b, 0x32, 0x4b, 0x2a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x9d, 0x24, 0x2e, 0x6d, 0xd1, 0x15, + 0x81, 0x9a, 0xee, 0x98, 0x92, 0x52, 0x94, 0x5a, 0x5c, 0x1c, 0x5c, 0x52, 0x94, 0x99, 0x97, 0x1e, + 0x84, 0x50, 0x2a, 0xe4, 0xc0, 0xc5, 0x56, 0x00, 0x36, 0x41, 0x82, 0x49, 0x81, 0x51, 0x83, 0xdb, + 0x48, 0x56, 0x0f, 0xab, 0xaf, 0xf5, 0x20, 0xd6, 0x38, 0x71, 0x9e, 0xb8, 0x27, 0xcf, 0xb0, 0xe2, + 0xf9, 0x06, 0x2d, 0xc6, 0x20, 0xa8, 0x3e, 0x2b, 0xa3, 0xa6, 0xe7, 0x1b, 0xb4, 0x10, 0x26, 0x76, + 0x3d, 0xdf, 0xa0, 0x25, 0x0f, 0xf1, 0x45, 0x05, 0xdc, 0x1f, 0x68, 0xae, 0x55, 0x92, 0xe4, 0x12, + 0x47, 0x13, 0x0a, 0x4a, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x35, 0x2a, 0xe1, 0x62, 0xf6, 0x2d, + 0x4e, 0x17, 0x4a, 0xe3, 0xe2, 0x41, 0xf1, 0x9f, 0x1a, 0x0e, 0x77, 0xa1, 0x19, 0x23, 0xa5, 0x47, + 0x9c, 0x3a, 0x98, 0x75, 0x52, 0xac, 0x0d, 0x20, 0xcf, 0x38, 0x79, 0x9d, 0x78, 0x24, 0xc7, 0x78, + 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, + 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x41, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, + 0xae, 0x7e, 0x72, 0x4e, 0x62, 0x71, 0x71, 0x66, 0xb2, 0x2e, 0xc4, 0x7b, 0xc9, 0xf9, 0x45, 0xa9, + 0xfa, 0x65, 0xc6, 0x48, 0xde, 0x2c, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, 0xc7, 0x92, 0x31, + 0x20, 0x00, 0x00, 0xff, 0xff, 0xd2, 0x9a, 0x8b, 0x48, 0x60, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/terra.tax2gas.v1beta1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/terra.tax2gas.v1beta1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "terra.tax2gas.v1beta1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "terra/tax2gas/v1beta1/tx.proto", +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/tax2gas/utils/fee_tax.go b/x/tax2gas/utils/fee_tax.go new file mode 100644 index 000000000..7a5272f62 --- /dev/null +++ b/x/tax2gas/utils/fee_tax.go @@ -0,0 +1,147 @@ +package utils + +import ( + "regexp" + "strings" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authz "github.com/cosmos/cosmos-sdk/x/authz" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + + marketexported "github.com/classic-terra/core/v3/x/market/exported" + "github.com/classic-terra/core/v3/x/tax2gas/types" +) + +var IBCRegexp = regexp.MustCompile("^ibc/[a-fA-F0-9]{64}$") + +func isIBCDenom(denom string) bool { + return IBCRegexp.MatchString(strings.ToLower(denom)) +} + +// FilterMsgAndComputeTax computes the stability tax on messages. +func FilterMsgAndComputeTax(ctx sdk.Context, tk types.TreasuryKeeper, burnTaxRate sdk.Dec, msgs ...sdk.Msg) sdk.Coins { + taxes := sdk.Coins{} + + for _, msg := range msgs { + switch msg := msg.(type) { + case *banktypes.MsgSend: + if !tk.HasBurnTaxExemptionAddress(ctx, msg.FromAddress, msg.ToAddress) { + taxes = taxes.Add(ComputeTax(burnTaxRate, msg.Amount)...) + } + + case *banktypes.MsgMultiSend: + tainted := 0 + + for _, input := range msg.Inputs { + if tk.HasBurnTaxExemptionAddress(ctx, input.Address) { + tainted++ + } + } + + for _, output := range msg.Outputs { + if tk.HasBurnTaxExemptionAddress(ctx, output.Address) { + tainted++ + } + } + + if tainted != len(msg.Inputs)+len(msg.Outputs) { + for _, input := range msg.Inputs { + taxes = taxes.Add(ComputeTax(burnTaxRate, input.Coins)...) + } + } + + case *marketexported.MsgSwapSend: + taxes = taxes.Add(ComputeTax(burnTaxRate, sdk.NewCoins(msg.OfferCoin))...) + + case *wasmtypes.MsgInstantiateContract: + taxes = taxes.Add(ComputeTax(burnTaxRate, msg.Funds)...) + + case *wasmtypes.MsgInstantiateContract2: + taxes = taxes.Add(ComputeTax(burnTaxRate, msg.Funds)...) + + case *wasmtypes.MsgExecuteContract: + if !tk.HasBurnTaxExemptionContract(ctx, msg.Contract) { + taxes = taxes.Add(ComputeTax(burnTaxRate, msg.Funds)...) + } + + case *authz.MsgExec: + messages, err := msg.GetMessages() + if err == nil { + taxes = taxes.Add(FilterMsgAndComputeTax(ctx, tk, burnTaxRate, messages...)...) + } + } + } + + return taxes +} + +// computes the stability tax according to tax-rate and tax-cap +func ComputeTax(burnTaxRate sdk.Dec, principal sdk.Coins) sdk.Coins { + taxes := sdk.Coins{} + + for _, coin := range principal { + if coin.Denom == sdk.DefaultBondDenom { + continue + } + + if isIBCDenom(coin.Denom) { + continue + } + + tax := sdk.NewDecFromInt(coin.Amount).Mul(burnTaxRate).TruncateInt() + if tax.Equal(sdk.ZeroInt()) { + continue + } + + taxes = taxes.Add(sdk.NewCoin(coin.Denom, tax)) + } + + return taxes +} + +func ComputeGas(gasPrices sdk.DecCoins, taxes sdk.Coins) (sdkmath.Int, error) { + taxes = taxes.Sort() + tax2gas := sdkmath.ZeroInt() + // Convert to gas + i, j := 0, 0 + for i < len(gasPrices) && j < len(taxes) { + switch { + case gasPrices[i].Denom == taxes[j].Denom: + tax2gas = tax2gas.Add(sdk.NewDec(taxes[j].Amount.Int64()).Quo((gasPrices[i].Amount)).Ceil().RoundInt()) + i++ + j++ + case gasPrices[i].Denom < taxes[j].Denom: + i++ + default: + j++ + } + } + + return tax2gas, nil +} + +func ComputeFeesOnGasConsumed(tx sdk.Tx, gasPrices sdk.DecCoins, gas sdkmath.Int) (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()) + + gasFees := make(sdk.Coins, len(gasPrices)) + if !isOracleTx && len(gasPrices) != 0 { + // Determine the required fees by multiplying each required minimum gas + // price by the gas limit, where fee = ceil(minGasPrice * gasLimit). + glDec := sdk.NewDecFromInt(gas) + for i, gp := range gasPrices { + fee := gp.Amount.Mul(glDec) + gasFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) + } + } + + return gasFees, nil +} diff --git a/x/tax2gas/utils/utils.go b/x/tax2gas/utils/utils.go new file mode 100644 index 000000000..f234d9163 --- /dev/null +++ b/x/tax2gas/utils/utils.go @@ -0,0 +1,102 @@ +package utils + +import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + oracleexported "github.com/classic-terra/core/v3/x/oracle/exported" +) + +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 +} + +// Find returns true and Dec amount if the denom exists in gasPrices. Otherwise it returns false +// and a zero dec. Uses binary search. +// CONTRACT: gasPrices must be valid (sorted). +func GetGasPriceByDenom(gasPrices sdk.DecCoins, denom string) (bool, sdk.Dec) { + switch len(gasPrices) { + case 0: + return false, sdk.ZeroDec() + + case 1: + gasPrice := gasPrices[0] + if gasPrice.Denom == denom { + return true, gasPrice.Amount + } + return false, sdk.ZeroDec() + + default: + midIdx := len(gasPrices) / 2 // 2:1, 3:1, 4:2 + gasPrice := gasPrices[midIdx] + switch { + case denom < gasPrice.Denom: + return GetGasPriceByDenom(gasPrices[:midIdx], denom) + case denom == gasPrice.Denom: + return true, gasPrice.Amount + default: + return GetGasPriceByDenom(gasPrices[midIdx+1:], denom) + } + } +} + +func CalculateTaxesAndPayableFee(gasPrices sdk.DecCoins, feeCoins sdk.Coins, taxGas sdkmath.Int, totalGasRemaining sdkmath.Int) (taxes, payableFees sdk.Coins, gasRemaining sdkmath.Int) { + taxGasRemaining := taxGas + taxes = sdk.NewCoins() + payableFees = sdk.NewCoins() + gasRemaining = totalGasRemaining + for _, feeCoin := range feeCoins { + found, gasPrice := GetGasPriceByDenom(gasPrices, feeCoin.Denom) + if !found { + continue + } + taxFeeRequired := sdk.NewCoin(feeCoin.Denom, gasPrice.MulInt(taxGasRemaining).Ceil().RoundInt()) + totalFeeRequired := sdk.NewCoin(feeCoin.Denom, gasPrice.MulInt(gasRemaining).Ceil().RoundInt()) + + switch { + case taxGasRemaining.IsPositive(): + switch { + case feeCoin.IsGTE(totalFeeRequired): + taxes = taxes.Add(taxFeeRequired) + payableFees = payableFees.Add(totalFeeRequired) + gasRemaining = sdkmath.ZeroInt() + return taxes, payableFees, gasRemaining + case feeCoin.IsGTE(taxFeeRequired): + taxes = taxes.Add(taxFeeRequired) + taxGasRemaining = sdkmath.ZeroInt() + payableFees = payableFees.Add(feeCoin) + totalFeeRemaining := sdk.NewDecCoinFromCoin(totalFeeRequired.Sub(feeCoin)) + gasRemaining = totalFeeRemaining.Amount.Quo(gasPrice).Ceil().RoundInt() + default: + taxes = taxes.Add(feeCoin) + payableFees = payableFees.Add(feeCoin) + taxFeeRemaining := sdk.NewDecCoinFromCoin(taxFeeRequired.Sub(feeCoin)) + taxGasRemaining = taxFeeRemaining.Amount.Quo(gasPrice).Ceil().RoundInt() + gasRemaining = gasRemaining.Sub(taxGas.Sub(taxGasRemaining)) + } + case gasRemaining.IsPositive(): + if feeCoin.IsGTE(totalFeeRequired) { + payableFees = payableFees.Add(totalFeeRequired) + gasRemaining = sdkmath.ZeroInt() + return taxes, payableFees, gasRemaining + } + payableFees = payableFees.Add(feeCoin) + totalFeeRemaining := sdk.NewDecCoinFromCoin(totalFeeRequired.Sub(feeCoin)) + gasRemaining = totalFeeRemaining.Amount.Quo(gasPrice).Ceil().RoundInt() + default: + return taxes, payableFees, gasRemaining + } + } + return taxes, payableFees, gasRemaining +}