Skip to content

Commit

Permalink
add feeburn module
Browse files Browse the repository at this point in the history
  • Loading branch information
trung2891 committed Nov 9, 2023
1 parent 4d19263 commit e56e870
Show file tree
Hide file tree
Showing 36 changed files with 3,023 additions and 6 deletions.
12 changes: 10 additions & 2 deletions ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import (
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmTypes "github.com/CosmWasm/wasmd/x/wasm/types"

feeburnAnte "github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/ante"
feeburnkeeper "github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/keeper"
migaloofeeante "github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/globalfee/ante"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
)

// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC
Expand All @@ -30,6 +33,8 @@ type HandlerOptions struct {
GlobalFeeSubspace paramtypes.Subspace
StakingSubspace paramtypes.Subspace
TXCounterStoreKey storetypes.StoreKey
FeeburnKeeper *feeburnkeeper.Keeper
BankKeeper bankkeeper.Keeper
}

func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) {
Expand All @@ -54,6 +59,9 @@ func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) {
if opts.GovKeeper == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "gov keeper is required for AnteHandler")
}
if opts.FeeburnKeeper == nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, "Fee burn keeper is required for ante builder")
}

sigGasConsumer := opts.SigGasConsumer
if sigGasConsumer == nil {
Expand All @@ -77,8 +85,8 @@ func NewAnteHandler(opts HandlerOptions) (sdk.AnteHandler, error) {
ante.NewConsumeGasForTxSizeDecorator(opts.AccountKeeper),
NewGovPreventSpamDecorator(opts.Codec, opts.GovKeeper),
migaloofeeante.NewFeeDecorator(opts.BypassMinFeeMsgTypes, opts.GlobalFeeSubspace, opts.StakingSubspace, maxBypassMinFeeMsgGasUsage),

ante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.TxFeeChecker),
feeburnAnte.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.TxFeeChecker, *opts.FeeburnKeeper),
// ante.NewDeductFeeDecorator(opts.AccountKeeper, opts.BankKeeper, opts.FeegrantKeeper, opts.TxFeeChecker),
ante.NewSetPubKeyDecorator(opts.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
ante.NewValidateSigCountDecorator(opts.AccountKeeper),
ante.NewSigGasConsumeDecorator(opts.AccountKeeper, sigGasConsumer),
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ module github.com/White-Whale-Defi-Platform/migaloo-chain/v3
go 1.21

require (
cosmossdk.io/errors v1.0.0-beta.7
cosmossdk.io/math v1.1.2
github.com/CosmWasm/wasmd v0.30.0
github.com/cosmos/cosmos-proto v1.0.0-beta.3
github.com/cosmos/cosmos-sdk v0.46.15
github.com/cosmos/gogoproto v1.4.8
github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6 v6.1.1-0.20230928173630-232c0f1cea39
github.com/cosmos/ibc-go/v6 v6.2.0
github.com/cosmos/interchain-accounts v0.4.3
Expand All @@ -25,6 +28,7 @@ require (
github.com/terra-money/core/v2 v2.4.1
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
google.golang.org/grpc v1.55.0
gopkg.in/yaml.v2 v2.4.0
)

require (
Expand All @@ -33,7 +37,6 @@ require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.13.0 // indirect
cloud.google.com/go/storage v1.28.1 // indirect
cosmossdk.io/errors v1.0.0-beta.7 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
Expand All @@ -55,9 +58,7 @@ require (
github.com/cometbft/cometbft-db v0.7.0 // indirect
github.com/confio/ics23/go v0.9.0 // indirect
github.com/cosmos/btcutil v1.0.5 // indirect
github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect
github.com/cosmos/go-bip39 v1.0.0 // indirect
github.com/cosmos/gogoproto v1.4.8 // indirect
github.com/cosmos/gorocksdb v1.2.0 // indirect
github.com/cosmos/iavl v0.19.6 // indirect
github.com/cosmos/ledger-cosmos-go v0.12.2 // indirect
Expand Down Expand Up @@ -163,7 +164,6 @@ require (
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
nhooyr.io/websocket v1.8.7 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
Expand Down
47 changes: 47 additions & 0 deletions testutil/keeper/feeburn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package keeper

import (
"testing"

"github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/keeper"
"github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/types"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/store"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmdb "github.com/tendermint/tm-db"
)

func FeeburnKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
storeKey := sdk.NewKVStoreKey(types.StoreKey)
memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey)

db := tmdb.NewMemDB()
stateStore := store.NewCommitMultiStore(db)
stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
require.NoError(t, stateStore.LoadLatestVersion())

registry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)

k := keeper.NewKeeper(
cdc,
storeKey,
memStoreKey,
authtypes.NewModuleAddress(govtypes.ModuleName),
)

ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger())

// Initialize params
_ = k.SetParams(ctx, types.DefaultParams())

return k, ctx
}
57 changes: 57 additions & 0 deletions testutil/nullify/nullify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Package nullify provides methods to init nil values structs for test assertion.
package nullify

import (
"reflect"
"unsafe"

sdk "github.com/cosmos/cosmos-sdk/types"
)

var (
coinType = reflect.TypeOf(sdk.Coin{})
coinsType = reflect.TypeOf(sdk.Coins{})
)

// Fill analyze all struct fields and slices with
// reflection and initialize the nil and empty slices,
// structs, and pointers.
func Fill(x interface{}) interface{} {
v := reflect.Indirect(reflect.ValueOf(x))
switch v.Kind() {
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
obj := v.Index(i)
objPt := reflect.NewAt(obj.Type(), unsafe.Pointer(obj.UnsafeAddr())).Interface()
objPt = Fill(objPt)
obj.Set(reflect.ValueOf(objPt))
}
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
f := reflect.Indirect(v.Field(i))
if !f.CanSet() {
continue
}
switch f.Kind() {
case reflect.Slice:
f.Set(reflect.MakeSlice(f.Type(), 0, 0))
case reflect.Struct:
switch f.Type() {
case coinType:
coin := reflect.New(coinType).Interface()
s := reflect.ValueOf(coin).Elem()
f.Set(s)
case coinsType:
coins := reflect.New(coinsType).Interface()
s := reflect.ValueOf(coins).Elem()
f.Set(s)
default:
objPt := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Interface()
s := Fill(objPt)
f.Set(reflect.ValueOf(s))
}
}
}
}
return reflect.Indirect(v).Interface()
}
13 changes: 13 additions & 0 deletions testutil/sample/sample.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package sample

import (
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// AccAddress returns a sample account address
func AccAddress() string {
pk := ed25519.GenPrivKey().PubKey()
addr := pk.Address()
return sdk.AccAddress(addr).String()
}
11 changes: 11 additions & 0 deletions x/feeburn/ante/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ante

import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// 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
BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
}
155 changes: 155 additions & 0 deletions x/feeburn/ante/fee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package ante

import (
"fmt"

errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/types"

feeburnkeeper "github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/keeper"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
)

// DeductFeeDecorator 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 DeductFeeDecorator struct {
accountKeeper ante.AccountKeeper
bankKeeper BankKeeper
feegrantKeeper ante.FeegrantKeeper
txFeeChecker ante.TxFeeChecker
feeburnKeeper feeburnkeeper.Keeper
}

func NewDeductFeeDecorator(ak ante.AccountKeeper, bk BankKeeper, fk ante.FeegrantKeeper, tfc ante.TxFeeChecker, fbk feeburnkeeper.Keeper) DeductFeeDecorator {
if tfc == nil {
tfc = checkTxFeeWithValidatorMinGasPrices
}

return DeductFeeDecorator{
accountKeeper: ak,
bankKeeper: bk,
feegrantKeeper: fk,
txFeeChecker: tfc,
feeburnKeeper: fbk,
}
}

func (dfd DeductFeeDecorator) 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
)

fee := feeTx.GetFee()
if !simulate {
fee, priority, err = dfd.txFeeChecker(ctx, tx)
if err != nil {
return ctx, err
}
}
if err := dfd.checkDeductFee(ctx, tx, fee); err != nil {
return ctx, err
}

newCtx := ctx.WithPriority(priority)

return next(newCtx, tx, simulate)
}

func (dfd DeductFeeDecorator) checkDeductFee(ctx sdk.Context, sdkTx sdk.Tx, fee sdk.Coins) error {
feeTx, ok := sdkTx.(sdk.FeeTx)
if !ok {
return errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}

if addr := dfd.accountKeeper.GetModuleAddress(types.FeeCollectorName); addr == nil {
return fmt.Errorf("fee collector module account (%s) has not been set", types.FeeCollectorName)
}

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 dfd.feegrantKeeper == nil {
return sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled")
} else if !feeGranter.Equals(feePayer) {
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, sdkTx.GetMsgs())
if err != nil {
return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", feeGranter, feePayer)
}
}

deductFeesFrom = feeGranter
}

deductFeesFromAcc := dfd.accountKeeper.GetAccount(ctx, deductFeesFrom)
if deductFeesFromAcc == nil {
return sdkerrors.ErrUnknownAddress.Wrapf("fee payer address: %s does not exist", deductFeesFrom)
}

// deduct the fees
if !fee.IsZero() {
feeBurnPercent, ok := sdk.NewIntFromString(dfd.feeburnKeeper.GetTxFeeBurnPercent(ctx))
if !ok {
return sdkerrors.ErrInvalidType
}
err := DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, fee, feeBurnPercent)
if err != nil {
return err
}
}

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 BankKeeper, ctx sdk.Context, acc types.AccountI, fees sdk.Coins, bp sdkmath.Int) error {
if !fees.IsValid() {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
}

// Calculate burning amounts by given percentage and fee amounts
burningFees := sdk.Coins{}
for _, fee := range fees {
burningAmount := fee.Amount.Mul(bp).Quo(sdk.NewInt(100))
burningFees = burningFees.Add(sdk.NewCoin(fee.Denom, burningAmount))
}

err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
}

err = bankKeeper.BurnCoins(ctx, types.FeeCollectorName, burningFees)
if err != nil {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFunds, err.Error())
}

return nil
}
Loading

0 comments on commit e56e870

Please sign in to comment.