-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e9592d3
commit cab04ab
Showing
32 changed files
with
2,966 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package ante | ||
|
||
import ( | ||
"math" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
sdkmath "cosmossdk.io/math" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
) | ||
|
||
// checkTxFeeWithValidatorMinGasPrices 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. | ||
func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error) { | ||
feeTx, ok := tx.(sdk.FeeTx) | ||
if !ok { | ||
return nil, 0, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") | ||
} | ||
|
||
feeCoins := feeTx.GetFee() | ||
gas := feeTx.GetGas() | ||
|
||
// 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() { | ||
minGasPrices := ctx.MinGasPrices() | ||
if !minGasPrices.IsZero() { | ||
requiredFees := 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 := sdkmath.LegacyNewDec(int64(gas)) | ||
for i, gp := range minGasPrices { | ||
fee := gp.Amount.Mul(glDec) | ||
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) | ||
} | ||
|
||
if !feeCoins.IsAnyGTE(requiredFees) { | ||
return nil, 0, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees) | ||
} | ||
} | ||
} | ||
|
||
priority := getTxPriority(feeCoins, int64(gas)) | ||
return feeCoins, 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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
// "strings" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
// "github.com/cosmos/cosmos-sdk/client/flags" | ||
// sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
"github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/types" | ||
) | ||
|
||
// GetQueryCmd returns the cli query commands for this module | ||
func GetQueryCmd(_ string) *cobra.Command { | ||
// Group feeburn queries under a subcommand | ||
cmd := &cobra.Command{ | ||
Use: types.ModuleName, | ||
Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), | ||
DisableFlagParsing: true, | ||
SuggestionsMinimumDistance: 2, | ||
RunE: client.ValidateCmd, | ||
} | ||
|
||
cmd.AddCommand(CmdQueryParams()) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package cli | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ChihuahuaChain/chihuahua/x/feeburn/types" | ||
Check failure on line 6 in x/feeburn/client/cli/query_params.go GitHub Actions / test
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func CmdQueryParams() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "params", | ||
Short: "shows the parameters of the module", | ||
Args: cobra.NoArgs, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
clientCtx := client.GetClientContextFromCmd(cmd) | ||
|
||
queryClient := types.NewQueryClient(clientCtx) | ||
|
||
res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return clientCtx.PrintProto(res) | ||
}, | ||
} | ||
|
||
flags.AddQueryFlagsToCmd(cmd) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
// "github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/ChihuahuaChain/chihuahua/x/feeburn/types" | ||
) | ||
|
||
// GetTxCmd returns the transaction commands for this module | ||
func GetTxCmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: types.ModuleName, | ||
Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), | ||
DisableFlagParsing: true, | ||
SuggestionsMinimumDistance: 2, | ||
RunE: client.ValidateCmd, | ||
} | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package feeburn | ||
|
||
import ( | ||
errorsmod "cosmossdk.io/errors" | ||
"github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/keeper" | ||
"github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
// InitGenesis initializes the module's state from a provided genesis state. | ||
func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { | ||
err := k.SetParams(ctx, genState.Params) | ||
if err != nil { | ||
panic(errorsmod.Wrapf(err, "error setting params")) | ||
} | ||
} | ||
|
||
// ExportGenesis returns the module's exported genesis | ||
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { | ||
genesis := types.DefaultGenesis() | ||
genesis.Params = k.GetParams(ctx) | ||
|
||
return genesis | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package feeburn_test | ||
|
||
import ( | ||
"testing" | ||
|
||
keepertest "github.com/ChihuahuaChain/chihuahua/testutil/keeper" | ||
"github.com/ChihuahuaChain/chihuahua/testutil/nullify" | ||
|
||
"github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn" | ||
"github.com/White-Whale-Defi-Platform/migaloo-chain/v3/x/feeburn/types" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestGenesis(t *testing.T) { | ||
genesisState := types.GenesisState{ | ||
Params: types.DefaultParams(), | ||
} | ||
|
||
k, ctx := keepertest.FeeburnKeeper(t) | ||
feeburn.InitGenesis(ctx, *k, genesisState) | ||
got := feeburn.ExportGenesis(ctx, *k) | ||
require.NotNil(t, got) | ||
|
||
nullify.Fill(&genesisState) | ||
nullify.Fill(got) | ||
} |
Oops, something went wrong.