Skip to content

Commit

Permalink
feat: Burn Tax via utilization of existing Stability Tax code (#784)
Browse files Browse the repository at this point in the history
* Burn Tax via utilization of existing Stability Tax code

* Added blockheights to the cosmos dependency
  • Loading branch information
edk208 committed Aug 9, 2022
1 parent f2417e1 commit b602d3e
Show file tree
Hide file tree
Showing 9 changed files with 534 additions and 14 deletions.
6 changes: 3 additions & 3 deletions custom/auth/ante/ante.go
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
@@ -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 {
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 {
// 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)
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
@@ -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
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
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
}
if coin.Denom == sdk.DefaultBondDenom {
continue
}

Expand Down

0 comments on commit b602d3e

Please sign in to comment.