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.
hikarid query dynamicfee --help
params
The params command allows users to query the on-chain parameters.
hikarid query dynamicfee params [flags]
Example:
hikarid 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.
hikarid query dynamicfee state [flags]
Example:
hikarid 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.
hikarid query dynamicfee gas-price [denom] [flags]
Example:
hikarid 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.
hikarid query dynamicfee gas-prices [flags]
Example:
hikarid 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"
}
]
}