Skip to content

Commit b602d3e

Browse files
authored
feat: Burn Tax via utilization of existing Stability Tax code (#784)
* Burn Tax via utilization of existing Stability Tax code * Added blockheights to the cosmos dependency
1 parent f2417e1 commit b602d3e

File tree

9 files changed

+534
-14
lines changed

9 files changed

+534
-14
lines changed

custom/auth/ante/ante.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@ import (
88
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
99
cosmosante "github.com/cosmos/cosmos-sdk/x/auth/ante"
1010
"github.com/cosmos/cosmos-sdk/x/auth/signing"
11-
"github.com/cosmos/cosmos-sdk/x/auth/types"
1211
)
1312

1413
// HandlerOptions are the options required for constructing a default SDK AnteHandler.
1514
type HandlerOptions struct {
1615
AccountKeeper cosmosante.AccountKeeper
17-
BankKeeper types.BankKeeper
16+
BankKeeper BankKeeper
1817
FeegrantKeeper cosmosante.FeegrantKeeper
1918
OracleKeeper OracleKeeper
2019
TreasuryKeeper TreasuryKeeper
@@ -62,7 +61,8 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
6261
cosmosante.NewValidateMemoDecorator(options.AccountKeeper),
6362
cosmosante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
6463
cosmosante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper),
65-
cosmosante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
64+
NewBurnTaxFeeDecorator(options.TreasuryKeeper, options.BankKeeper), // burn tax proceeds
65+
cosmosante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
6666
cosmosante.NewValidateSigCountDecorator(options.AccountKeeper),
6767
cosmosante.NewSigGasConsumeDecorator(options.AccountKeeper, sigGasConsumer),
6868
NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),

custom/auth/ante/burntax.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package ante
2+
3+
import (
4+
sdk "github.com/cosmos/cosmos-sdk/types"
5+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
6+
"github.com/cosmos/cosmos-sdk/x/auth/types"
7+
treasury "github.com/terra-money/core/x/treasury/types"
8+
)
9+
10+
// TaxPowerUpgradeHeight is when taxes are allowed to go into effect
11+
// This will still need a parameter change proposal, but can be activated
12+
// anytime after this height
13+
const TaxPowerUpgradeHeight = 9346889
14+
15+
// BurnTaxFeeDecorator will immediately burn the collected Tax
16+
type BurnTaxFeeDecorator struct {
17+
treasuryKeeper TreasuryKeeper
18+
bankKeeper BankKeeper
19+
}
20+
21+
// NewBurnTaxFeeDecorator returns new tax fee decorator instance
22+
func NewBurnTaxFeeDecorator(treasuryKeeper TreasuryKeeper, bankKeeper BankKeeper) BurnTaxFeeDecorator {
23+
return BurnTaxFeeDecorator{
24+
treasuryKeeper: treasuryKeeper,
25+
bankKeeper: bankKeeper,
26+
}
27+
}
28+
29+
// AnteHandle handles msg tax fee checking
30+
func (btfd BurnTaxFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
31+
// Do not proceed if you are below this block height
32+
currHeight := ctx.BlockHeight()
33+
if currHeight < TaxPowerUpgradeHeight {
34+
return next(ctx, tx, simulate)
35+
}
36+
37+
feeTx, ok := tx.(sdk.FeeTx)
38+
if !ok {
39+
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
40+
}
41+
42+
msgs := feeTx.GetMsgs()
43+
44+
// At this point we have already run the DeductFees AnteHandler and taken the fees from the sending account
45+
// Now we remove the taxes from the gas reward and immediately burn it
46+
47+
if !simulate {
48+
// Compute taxes again.
49+
taxes := FilterMsgAndComputeTax(ctx, btfd.treasuryKeeper, msgs...)
50+
51+
// Record tax proceeds
52+
if !taxes.IsZero() {
53+
err = btfd.bankKeeper.SendCoinsFromModuleToModule(ctx, types.FeeCollectorName, treasury.BurnModuleName, taxes)
54+
if err != nil {
55+
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
56+
}
57+
}
58+
}
59+
60+
return next(ctx, tx, simulate)
61+
}

custom/auth/ante/burntax_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package ante_test
2+
3+
import (
4+
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
5+
"github.com/cosmos/cosmos-sdk/testutil/testdata"
6+
sdk "github.com/cosmos/cosmos-sdk/types"
7+
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
8+
9+
"github.com/terra-money/core/custom/auth/ante"
10+
core "github.com/terra-money/core/types"
11+
"github.com/cosmos/cosmos-sdk/types/query"
12+
"github.com/cosmos/cosmos-sdk/x/auth/types"
13+
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
14+
)
15+
16+
func (suite *AnteTestSuite) TestEnsureBurnTaxModule() {
17+
suite.SetupTest(true) // setup
18+
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder()
19+
20+
mfd := ante.NewBurnTaxFeeDecorator(suite.app.TreasuryKeeper, suite.app.BankKeeper)
21+
antehandler := sdk.ChainAnteDecorators(mfd)
22+
23+
// keys and addresses
24+
priv1, _, addr1 := testdata.KeyTestPubAddr()
25+
26+
// msg and signatures
27+
sendAmount := int64(1000000)
28+
sendCoins := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, sendAmount))
29+
msg := banktypes.NewMsgSend(addr1, addr1, sendCoins)
30+
31+
feeAmount := testdata.NewTestFeeAmount()
32+
gasLimit := testdata.NewTestGasLimit()
33+
suite.Require().NoError(suite.txBuilder.SetMsgs(msg))
34+
suite.txBuilder.SetFeeAmount(feeAmount)
35+
suite.txBuilder.SetGasLimit(gasLimit)
36+
37+
privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0}
38+
tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID())
39+
suite.Require().NoError(err)
40+
41+
// set zero gas prices
42+
suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins())
43+
44+
// Set IsCheckTx to true
45+
suite.ctx = suite.ctx.WithIsCheckTx(true)
46+
47+
// Luna must pass without burn before the specified tax block height
48+
_, err = antehandler(suite.ctx, tx, false)
49+
suite.Require().NoError(err, "Decorator should not have errored when block height is 1")
50+
51+
// Set the blockheight past the tax height block
52+
suite.ctx = suite.ctx.WithBlockHeight(10000000)
53+
// antehandler errors with insufficient fees due to tax
54+
_, err = antehandler(suite.ctx, tx, false)
55+
suite.Require().Error(err, "Decorator should errored on low fee for local gasPrice + tax")
56+
57+
tk := suite.app.TreasuryKeeper
58+
expectedTax := tk.GetTaxRate(suite.ctx).MulInt64(sendAmount).TruncateInt()
59+
if taxCap := tk.GetTaxCap(suite.ctx, core.MicroSDRDenom); expectedTax.GT(taxCap) {
60+
expectedTax = taxCap
61+
}
62+
63+
taxes := sdk.NewCoins(sdk.NewInt64Coin(core.MicroSDRDenom, expectedTax.Int64()))
64+
65+
bk := suite.app.BankKeeper
66+
bk.MintCoins(suite.ctx, minttypes.ModuleName, sendCoins)
67+
68+
// Populate the FeeCollector module with taxes
69+
bk.SendCoinsFromModuleToModule(suite.ctx, minttypes.ModuleName, types.FeeCollectorName, taxes)
70+
feeCollector := suite.app.AccountKeeper.GetModuleAccount(suite.ctx, types.FeeCollectorName)
71+
72+
amountFee := bk.GetAllBalances(suite.ctx, feeCollector.GetAddress())
73+
suite.Require().Equal(amountFee, taxes)
74+
totalSupply, _, err := bk.GetPaginatedTotalSupply(suite.ctx, &query.PageRequest{})
75+
76+
// must pass with tax and burn
77+
_, err = antehandler(suite.ctx, tx, false)
78+
suite.Require().NoError(err, "Decorator should not have errored on fee higher than local gasPrice")
79+
80+
// Burn the taxes
81+
tk.BurnCoinsFromBurnAccount(suite.ctx)
82+
suite.Require().NoError(err)
83+
84+
supplyAfterBurn, _, err := bk.GetPaginatedTotalSupply(suite.ctx, &query.PageRequest{})
85+
86+
// Total supply should have decreased by the tax amount
87+
suite.Require().Equal(taxes, totalSupply.Sub(supplyAfterBurn))
88+
89+
90+
}
91+

custom/auth/ante/expected_keeper.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ type TreasuryKeeper interface {
1515
type OracleKeeper interface {
1616
ValidateFeeder(ctx sdk.Context, feederAddr sdk.AccAddress, validatorAddr sdk.ValAddress) error
1717
}
18+
19+
// BankKeeper defines the contract needed for supply related APIs (noalias)
20+
type BankKeeper interface {
21+
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
22+
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule string, recipientModule string, amt sdk.Coins) error
23+
}

custom/auth/ante/tax.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,19 @@ func FilterMsgAndComputeTax(ctx sdk.Context, tk TreasuryKeeper, msgs ...sdk.Msg)
144144

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

152153
taxes := sdk.Coins{}
153154
for _, coin := range principal {
154-
if coin.Denom == core.MicroLunaDenom || coin.Denom == sdk.DefaultBondDenom {
155+
// Originally only a stability tax on UST. Changed to tax Luna as well after TaxPowerUpgradeHeight
156+
if (coin.Denom == core.MicroLunaDenom || coin.Denom == sdk.DefaultBondDenom) && currHeight < TaxPowerUpgradeHeight {
157+
continue
158+
}
159+
if coin.Denom == sdk.DefaultBondDenom {
155160
continue
156161
}
157162

0 commit comments

Comments
 (0)