Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Burn Tax via utilization of existing Stability Tax code #784

Merged
merged 2 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions custom/auth/ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
cosmosante "github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)

// HandlerOptions are the options required for constructing a default SDK AnteHandler.
type HandlerOptions struct {
AccountKeeper cosmosante.AccountKeeper
BankKeeper types.BankKeeper
BankKeeper BankKeeper
FeegrantKeeper cosmosante.FeegrantKeeper
OracleKeeper OracleKeeper
TreasuryKeeper TreasuryKeeper
Expand Down Expand Up @@ -62,7 +61,8 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
cosmosante.NewValidateMemoDecorator(options.AccountKeeper),
cosmosante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
cosmosante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper),
cosmosante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
NewBurnTaxFeeDecorator(options.TreasuryKeeper, options.BankKeeper), // burn tax proceeds
cosmosante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
cosmosante.NewValidateSigCountDecorator(options.AccountKeeper),
cosmosante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer),
NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
Expand Down
61 changes: 61 additions & 0 deletions custom/auth/ante/burntax.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package ante

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/types"
treasury "github.com/terra-money/core/x/treasury/types"
)

// TaxPowerUpgradeHeight is when taxes are allowed to go into effect
// This will still need a parameter change proposal, but can be activated
// anytime after this height
const TaxPowerUpgradeHeight = 9346889

// BurnTaxFeeDecorator will immediately burn the collected Tax
type BurnTaxFeeDecorator struct {
treasuryKeeper TreasuryKeeper
bankKeeper BankKeeper
}

// NewBurnTaxFeeDecorator returns new tax fee decorator instance
func NewBurnTaxFeeDecorator(treasuryKeeper TreasuryKeeper, bankKeeper BankKeeper) BurnTaxFeeDecorator {
return BurnTaxFeeDecorator{
treasuryKeeper: treasuryKeeper,
bankKeeper: bankKeeper,
}
}

// AnteHandle handles msg tax fee checking
func (btfd BurnTaxFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
// Do not proceed if you are below this block height
currHeight := ctx.BlockHeight()
if currHeight < TaxPowerUpgradeHeight {
yun-yeo marked this conversation as resolved.
Show resolved Hide resolved
return next(ctx, tx, simulate)
}

feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}

msgs := feeTx.GetMsgs()

// At this point we have already run the DeductFees AnteHandler and taken the fees from the sending account
// Now we remove the taxes from the gas reward and immediately burn it

if !simulate {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't seem to have any test with simulate=true, wonder what happens in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the tx is simulated, it does not perform the fee module to burn module transfer for the tax and just moves to the next AnteHandler. Do you believe there should be a simulate case? Thanks!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK didn't know that, no sense to add a test in this case then!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simcheolhwan can you please merge this PR

// Compute taxes again.
taxes := FilterMsgAndComputeTax(ctx, btfd.treasuryKeeper, msgs...)

// Record tax proceeds
if !taxes.IsZero() {
err = btfd.bankKeeper.SendCoinsFromModuleToModule(ctx, types.FeeCollectorName, treasury.BurnModuleName, taxes)
yun-yeo marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
}
}
}

return next(ctx, tx, simulate)
}
91 changes: 91 additions & 0 deletions custom/auth/ante/burntax_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package ante_test

import (
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

"github.com/terra-money/core/custom/auth/ante"
core "github.com/terra-money/core/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/auth/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)

func (suite *AnteTestSuite) TestEnsureBurnTaxModule() {
suite.SetupTest(true) // setup
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()

mfd := ante.NewBurnTaxFeeDecorator(suite.app.TreasuryKeeper, suite.app.BankKeeper)
antehandler := sdk.ChainAnteDecorators(mfd)

// keys and addresses
priv1, _, addr1 := testdata.KeyTestPubAddr()

// 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()
suite.Require().NoError(suite.txBuilder.SetMsgs(msg))
suite.txBuilder.SetFeeAmount(feeAmount)
suite.txBuilder.SetGasLimit(gasLimit)

privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID())
suite.Require().NoError(err)

// set zero gas prices
suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins())

// Set IsCheckTx to true
suite.ctx = suite.ctx.WithIsCheckTx(true)

// Luna must pass without burn before the specified tax block height
_, err = antehandler(suite.ctx, tx, false)
suite.Require().NoError(err, "Decorator should not have errored when block height is 1")

// Set the blockheight past the tax height block
suite.ctx = suite.ctx.WithBlockHeight(10000000)
// antehandler errors with insufficient fees due to tax
_, err = antehandler(suite.ctx, tx, false)
suite.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax")

tk := suite.app.TreasuryKeeper
expectedTax := tk.GetTaxRate(suite.ctx).MulInt64(sendAmount).TruncateInt()
if taxCap := tk.GetTaxCap(suite.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) {
expectedTax = taxCap
}

taxes := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, expectedTax.Int64()))

bk := suite.app.BankKeeper
bk.MintCoins(suite.ctx, minttypes.ModuleName, sendCoins)

// Populate the FeeCollector module with taxes
bk.SendCoinsFromModuleToModule(suite.ctx, minttypes.ModuleName, types.FeeCollectorName, taxes)
feeCollector := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, types.FeeCollectorName)

amountFee := bk.GetAllBalances(suite.ctx, feeCollector.GetAddress())
suite.Require().Equal(amountFee, taxes)
totalSupply, _, err := bk.GetPaginatedTotalSupply(suite.ctx, &query.PageRequest{})

// must pass with tax and burn
_, err = antehandler(suite.ctx, tx, false)
suite.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice")

// Burn the taxes
tk.BurnCoinsFromBurnAccount(suite.ctx)
suite.Require().NoError(err)

supplyAfterBurn, _, err := bk.GetPaginatedTotalSupply(suite.ctx, &query.PageRequest{})

// Total supply should have decreased by the tax amount
suite.Require().Equal(taxes, totalSupply.Sub(supplyAfterBurn))


}

6 changes: 6 additions & 0 deletions custom/auth/ante/expected_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ type TreasuryKeeper interface {
type OracleKeeper interface {
ValidateFeeder(ctx sdk.Context, feederAddr sdk.AccAddress, validatorAddr sdk.ValAddress) error
}

// BankKeeper defines the contract needed for supply related APIs (noalias)
type BankKeeper interface {
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
}
7 changes: 6 additions & 1 deletion custom/auth/ante/tax.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,19 @@ func FilterMsgAndComputeTax(ctx sdk.Context, tk TreasuryKeeper, msgs ...sdk.Msg)

// computes the stability tax according to tax-rate and tax-cap
func computeTax(ctx sdk.Context, tk TreasuryKeeper, principal sdk.Coins) sdk.Coins {
currHeight := ctx.BlockHeight()
taxRate := tk.GetTaxRate(ctx)
if taxRate.Equal(sdk.ZeroDec()) {
return sdk.Coins{}
}

taxes := sdk.Coins{}
for _, coin := range principal {
if coin.Denom == core.MicroLunaDenom || coin.Denom == sdk.DefaultBondDenom {
// Originally only a stability tax on UST. Changed to tax Luna as well after TaxPowerUpgradeHeight
if (coin.Denom == core.MicroLunaDenom || coin.Denom == sdk.DefaultBondDenom) && currHeight < TaxPowerUpgradeHeight {
continue
yun-yeo marked this conversation as resolved.
Show resolved Hide resolved
}
if coin.Denom == sdk.DefaultBondDenom {
continue
}

Expand Down
Loading