x/dynamicfee
Abstract
This document describes the specifications of the AtomOne implementation of the x/dynamicfee
module. This module is a fork of the skip-mev/feemarket
module (more specifically, of the sdk-47
branch) and includes changes and adaptations to suit the AtomOne project.
Contents
Concepts
Additive Increase Multiplicative Decrease (AIMD) EIP-1559
Please refer to AIMD.md for a detailed description of the AIMD EIP-1559
Fee deduction and naive Tx prioritization
Fee deduction is performed in the anteHandler
. The entire user-set fee is deducted from the user's account and sent to the x/distribution
module account. In order for a transaction to be included in a block, the transaction's gas price must be at least equat to the current gas price. However, users can also specify an even higher gas price than the current gas price to increase the priority of their transaction. A naive form of transactions prioritization is implemented so that transactions with higher gas prices are included in the block with higher priority.
Module state updates
The dynamicfee
module updates the gas consumed in the current block on a per-tx basis relying on the postHandler
. Updates to the base fee and learning rate are instead performed in the endBlocker
.
State
The x/dynamicfee
module keeps state of the following primary objects:
- Current base-fee
- Current learning rate
- Moving window of block gas
In addition, the x/dynamicfee
module keeps the following indexes to manage the aforementioned state:
- State:
0x02 |ProtocolBuffer(State)
GasPrice
GasPrice is the current gas price. This is denominated in the fee per gas unit in the base fee denom.
LearningRate
LearningRate is the current learning rate.
Window
Window contains a list of the last blocks' gas values. This is used to calculate the next base fee. This stores the number of units of gas consumed per block.
Index
Index is the index of the current block in the block gas window.
// State is utilized to track the current state of the dynamic fee pricer. This
// includes the current base fee, learning rate, and block gas within the
// specified AIMD window.
message State {
// BaseGasPrice is the current base fee. This is denominated in the fee per gas
// unit.
string base_gas_price = 1 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// LearningRate is the current learning rate.
string learning_rate = 2 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// Window contains a list of the last blocks' gas values. This is used
// to calculate the next base fee. This stores the number of units of gas
// consumed per block.
repeated uint64 window = 3;
// Index is the index of the current block in the block gas window.
uint64 index = 4;
}
Keeper
The dynamicfee module provides a keeper interface for accessing the KVStore.
type DynamicfeeKeeper interface {
// Get the current state from the store.
GetState(ctx sdk.Context) (types.State, error)
// Set the state in the store.
SetState(ctx sdk.Context, state types.State) error
// Get the current params from the store.
GetParams(ctx sdk.Context) (types.Params, error)
// Set the params in the store.
SetParams(ctx sdk.Context, params types.Params) error
// Get the minimum gas price for a given denom from the store.
GetMinGasPrice(ctx sdk.Context, denom string) (sdk.DecCoin, error) {
// Get the current minimum gas prices from the store.
GetMinGasPrices(ctx sdk.Context) (sdk.DecCoins, error)
}
Messages
MsgParams
The dynamicfee
module params can be updated through MsgParams
, which can be done using a governance proposal. The signer will always be the gov
module account address.
message MsgParams {
option (cosmos.msg.v1.signer) = "authority";
// Params defines the new parameters for the dynamicfee module.
Params params = 1 [ (gogoproto.nullable) = false ];
// Authority defines the authority that is updating the dynamicfee module
// parameters.
string authority = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}
The message handling can fail if:
- signer is not the gov module account address.
Events
The dynamicfee module emits the following events:
Tx
{
"type": "tx",
"attributes": [
{
"key": "fee",
"value": "{{sdk.Coins being payed}}",
"index": true
},
{
"key": "fee_payer",
"value": "{{sdk.AccAddress paying the fees}}",
"index": true
}
]
}
Parameters
The dynamicfee module stores its params in state with the prefix of 0x01
, which can be updated with governance or the address with authority.
- Params:
0x01 | ProtocolBuffer(Params)
The dynamicfee module contains the following parameters:
Alpha
Alpha is the amount we added to the learning rate when it is above or below the target +/- threshold.
Beta
Beta is the amount we multiplicatively decrease the learning rate when it is within the target +/- threshold.
Gamma
Gamma is the threshold for the learning rate. If the learning rate is above or below the target +/- threshold, we additively increase the learning rate by Alpha. Otherwise, we multiplicatively decrease the learning rate by Beta.
MinBaseGasPrice
MinBaseGasPrice determines the initial gas price of the module and the global minimum for the network. This is denominated in fee per gas unit in the FeeDenom
.
TargetBlockUtilization
TargetBlockUtilization is the target block utilization expressed as a percentage of the block gas limit.
MinLearningRate
MinLearningRate is the lower bound for the learning rate.
MaxLearningRate
MaxLearningRate is the upper bound for the learning rate.
Window
Window defines the window size for calculating an adaptive learning rate over a moving window of blocks. The default EIP1559 implementation uses a window of size 1.
FeeDenom
FeeDenom is the denom that will be used for all fee payments.
Enabled
Enabled is a boolean that determines whether the EIP1559 dynamic fee pricing is enabled. This can be used to add the dynamicfee module and enable it through governance at a later time.
// Params contains the required set of parameters for the EIP1559 dynamic fee
// pricing implementation.
message Params {
// Alpha is the amount we additively increase the learning rate
// when it is above or below the target +/- threshold.
//
// Must be > 0.
string alpha = 1 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// Beta is the amount we multiplicatively decrease the learning rate
// when it is within the target +/- threshold.
//
// Must be [0, 1].
string beta = 2 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// Gamma is the threshold for the learning rate. If the learning rate is
// above or below the target +/- threshold, we additively increase the
// learning rate by Alpha. Otherwise, we multiplicatively decrease the
// learning rate by Beta.
//
// Must be [0, 0.5].
string gamma = 3 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// MinBaseGasPrice determines the initial gas price of the module and the
// global minimum for the network.
string min_base_gas_price = 5 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// TargetBlockUtilization is the target block utilization expressed as a
// decimal value between 0 and 1. It is the target percentage utilization
// of the block in relation to the consensus_params.block.max_gas parameter.
string target_block_utilization = 6 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// MinLearningRate is the lower bound for the learning rate.
string min_learning_rate = 7 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// MaxLearningRate is the upper bound for the learning rate.
string max_learning_rate = 8 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
];
// Window defines the window size for calculating an adaptive learning rate
// over a moving window of blocks.
uint64 window = 9;
// FeeDenom is the denom that will be used for all fee payments.
string fee_denom = 10;
// Enabled is a boolean that determines whether the EIP1559 dynamic fee
// pricing is enabled.
bool enabled = 11;
}
Client
CLI
A user can query and interact with the dynamicfee
module using the CLI.
Query
The query
commands allow users to query dynamicfee
state.
atomoned query dynamicfee --help
params
The params
command allows users to query the on-chain parameters.
atomoned query dynamicfee params [flags]
Example:
atomoned query dynamicfee params
Example Output:
alpha: "0.000000000000000000"
beta: "1.000000000000000000"
enabled: true
fee_denom: uatone
gamma: "0.000000000000000000"
max_learning_rate: "0.125000000000000000"
min_base_gas_price: "1.000000000000000000"
min_learning_rate: "0.125000000000000000"
target_block_utilization: "0.500000000000000000"
window: "1"
state
The state
command allows users to query the current on-chain state.
atomoned query dynamicfee state [flags]
Example:
atomoned query dynamicfee state
Example Output:
base_fee: "1.000000000000000000"
index: "0"
learning_rate: "0.125000000000000000"
window:
- "0"
gas-price
The gas-price
command allows users to query the current gas-price for a given denom.
atomoned query dynamicfee gas-price [denom] [flags]
Example:
atomoned query dynamicfee gas-price uatone
Example Output:
1000000uatone
gas-prices
The gas-prices
command allows users to query the current gas-price for all supported denoms.
atomoned query dynamicfee gas-prices [flags]
Example:
atomoned query dynamicfee gas-prices
Example Output:
1000000stake,100000uatone
gRPC
A user can query the dynamicfee
module using gRPC endpoints.
Params
The Params
endpoint allows users to query the on-chain parameters.
atomone.dynamicfee.v1.Query/Params
Example:
grpcurl -plaintext \
localhost:9090 \
atomone.dynamicfee.v1.Query/Params
Example Output:
{
"params": {
"alpha": "0",
"beta": "1000000000000000000",
"gamma": "0",
"minBaseGasPrice": "1000000",
"minLearningRate": "125000000000000000",
"maxLearningRate": "125000000000000000",
"targetBlockUtilization": "500000000000000000",
"window": "1",
"feeDenom": "uatone",
"enabled": true
}
}
State
The State
endpoint allows users to query the current on-chain state.
atomone.dynamicfee.v1.Query/State
Example:
grpcurl -plaintext \
localhost:9090 \
atomone.dynamicfee.v1.Query/State
Example Output:
{
"state": {
"baseGasPrice": "1000000",
"learningRate": "125000000000000000",
"window": [
"0"
]
}
}
GasPrice
The GasPrice
endpoint allows users to query the current on-chain gas price for a given denom.
atomone.dynamicfee.v1.Query/GasPrice
Example:
grpcurl -plaintext \
-d '{"denom": "uatone"}' \
localhost:9090 \
atomone.dynamicfee.v1.Query/GasPrice/
Example Output:
{
"price": {
"denom": "uatone",
"amount": "1000000"
}
}
GasPrices
The GasPrices
endpoint allows users to query the current on-chain gas prices for all denoms.
atomone.dynamicfee.v1.Query/GasPrices
Example:
grpcurl -plaintext \
localhost:9090 \
atomone.dynamicfee.v1.Query/GasPrices
Example Output:
{
"prices": [
{
"denom": "uatone",
"amount": "1000000"
}
]
}