This repository has been archived by the owner on Aug 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #162 from blukat29/priority-fee-mechanism
Propose KIP-162 Priority Fee Mechanism
- Loading branch information
Showing
1 changed file
with
204 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,204 @@ | ||
--- | ||
kip: 162 | ||
title: Priority Fee Mechanism | ||
author: Ollie (@blukat29), Sawyer (@2dvorak), Aidan (@aidan-kwon) | ||
status: Draft | ||
discussions-to: https://github.com/klaytn/kips/issues/161 | ||
type: Standard Track | ||
category : Core | ||
created: 2024-03-20 | ||
--- | ||
|
||
# Simple summary | ||
|
||
Priority fee mechanism for transaction type 2 (EthereumDynamicFee) in terms of transaction ordering. | ||
|
||
# Abstract | ||
|
||
Activate the priority fee mechanism as defined in the EIP-1559. The maxPriorityFeePerGas field of transaction type 2 is no longer a placeholder, now it represents the priority fee the sender is willing to pay. The transactions are ordered by descending effective gas price, which is usually in the descending priority fee order. Under congested network, high priority transactions can be included to block by declaring high priority fee. | ||
|
||
# Motivation | ||
|
||
In Klaytn, the KIP-71 dynamic base fee mechanism and the FCFS (first come first serve) policy had been introduced to alleviate the network stability and usability issue caused by transaction bursts. However, if there are enough transactions that are willing to pay the upper bound base fee (e.g. 750 ston) then the network traffic will stay contested. Under such a condition, senders may experience transaction delay even if they pay the highest fee and send the transactions as soon as possible. | ||
|
||
Raising the base fee upper bound is undesirable solution because the users no longer be able to predict or budget their gas fee spendings. Instead, this proposal adds a reliable method for urgent transactions to be included in blocks. | ||
|
||
# Specification | ||
|
||
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC-2119](https://www.ietf.org/rfc/rfc2119.txt). | ||
|
||
## Block processing | ||
|
||
### Configuration | ||
|
||
The KIP-162 shall be activated since the `DRAGON_FORK_BLOCK_NUMBER`. | ||
|
||
| config | value | | ||
|-|-| | ||
| `MAGMA_FORK_BLOCK_NUMBER` | 98347376 (testnet), 99841497 (mainnet) | | ||
| `DRAGON_FORK_BLOCK_NUMBER` | TBD | | ||
|
||
### Effective gas price and effective priority fee per gas | ||
|
||
The effective gas price and effective priorirty fee per gas of a transaction at given block are calculated as follows. The effective gas price is the actual price of gas fee paid by the sender. The calculation differs by whether the transaction type is 2 (EIP-1559 EthereumDynamicFee) or not. | ||
|
||
```py | ||
def EffectiveGasPrice(header: Header, tx: Transaction): | ||
if header.number >= DRAGON_FORK_BLOCK_NUMBER: | ||
# Transaction type 2 has {maxFeePerGas, maxPriorityFeePerGas} fields | ||
# Other transactions only have {gasPrice} field | ||
if tx.type == 2: | ||
maxFeePerGas = tx.maxFeePerGas | ||
maxPriorityFeePerGas = tx.maxPriorityFeePerGas | ||
else: | ||
# makes EffectiveGasPrice() = tx.gasPrice | ||
maxFeePerGas = tx.gasPrice | ||
maxPriorityFeePerGas = tx.gasPrice | ||
return min(header.baseFeePerGas + maxPriorityFeePerGas, maxFeePerGas) | ||
|
||
elif header.number >= MAGMA_FORK_BLOCK_NUMBER: | ||
return header.baseFeePerGas | ||
|
||
else: | ||
# Note: before Magma hardfork, transactions must satisfy | ||
# tx.gasPrice == config.UnitPrice or tx.maxFeePerGas == config.UnitPrice. | ||
# Therefore EffectiveGasPrice is essentially config.UnitPrice. | ||
if tx.type == 2: | ||
return tx.maxFeePerGas | ||
else: | ||
return tx.gasPrice | ||
|
||
def EffectivePriorityFeePerGas(header: Header, tx: Transaction): | ||
if header.number >= DRAGON_FORK_BLOCK_NUMBER: | ||
effectiveGasPrice = EffectiveGasPrice(header, tx) | ||
return effectiveGasPrice - header.baseFeePerGas | ||
else: | ||
return 0 | ||
``` | ||
|
||
### KIP-82 reward scheme | ||
|
||
The [KIP-82](https://github.com/klaytn/kips/blob/main/KIPs/kip-82.md) reward scheme is slightly modified. The `get_total_fee` now accounts for effective gas price. Otherwise remains the same. | ||
|
||
```py | ||
def get_total_fee(header: Header, txs: list[Transaction], receipts: list[Receipt]): | ||
# Note: EffectiveGasPrice handles hardfork | ||
totalFee = 0 | ||
for i in range(len(txs)): | ||
totalFee += EffectiveGasPrice(header, txs[i]) * receipts[i].gasUsed | ||
return totalFee | ||
``` | ||
|
||
### EVM GASPRICE opcode | ||
|
||
The `GASPRICE` (0x3a) opcode returns the effective gas price of the currently executing transaction. | ||
|
||
## Transaction pool | ||
|
||
### Transaction ordering | ||
|
||
Block transaction ordering depends on client implementation as the ordering is not checked by block validators. However, under the KIP-82 reward scheme, a proposer should prioritize high priority fee transactions for the maximum proposer reward. | ||
|
||
Block proposers are recommended to order transactions with descending effective gas price, and the same effective gas price are sorted by transaction arrival time. This discourages the transaction spamming where a sender sends many transactions hoping that at least one is included in the block. Instead, the sender would pay a higher priority fee to ensure the transaction be included in the block. | ||
|
||
### Txpool management | ||
|
||
A transaction pool should not accept underpriced transactions. A transaction is underpriced if its `tx.gasPrice` or `tx.maxFeePerGas` field is lower than the baseFeePerGas of the pending block. | ||
|
||
## JSON-RPC API | ||
|
||
### `eth_gasPrice` and `klay_gasPrice` | ||
|
||
Returns suggested value for `gasPrice` or `maxFeePerGas` fields of a transaction. | ||
|
||
- Parameters: none | ||
- Result: Recommended gas price in peb. The result depends on the client implementation. | ||
- Example | ||
```sh | ||
curl $RPC_URL -X POST -H 'Content-Type: application/json' --data ' | ||
{"jsonrpc":"2.0","id":1,"method":"eth_gasPrice","params":[]}' | ||
|
||
{"jsonrpc":"2.0","id":1,"result":"0xba43b7400"} | ||
``` | ||
|
||
### `eth_maxPriorityFeePerGas` and `klay_maxPriorityFeePerGas` | ||
|
||
Returns suggested value for `gasPrice` or `maxFeePerGas` fields of a transaction. | ||
|
||
- Parameters: none | ||
- Result: Recommended priority fee per gas in peb. The result depends on the client implementation. | ||
- Example | ||
```sh | ||
curl $RPC_URL -X POST -H 'Content-Type: application/json' --data ' | ||
{"jsonrpc":"2.0","id":1,"method":"eth_maxPriorityFeePerGas","params":[]}' | ||
|
||
{"jsonrpc":"2.0","id":1,"result":"0x3b9aca00"} | ||
``` | ||
|
||
### `eth_feeHistory` and `klay_feeHistory` | ||
|
||
Returns historical gas information for a range of blocks. | ||
|
||
- Parameters | ||
- `blockCount` - Number of blocks in the requested range. | ||
- `newestBlock` - Highest block of the requested range. Can be a number or "latest". | ||
- `rewardPercentiles` - (optional) A monotonically increasing list of percentile (between 0 and 100) values. For each block in the requested range, the transactions will be sorted in ascending order by effective tip per gas and sampled at the specified percentiles. | ||
- Result | ||
- `oldestBlock` - Lowest number block of returned range. | ||
- `baseFeePerGas` - An array of block base fee per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-Magma blocks. | ||
- `gasUsedRatio` - An array of block gas used ratios. Measures the network congestion level. These are calculated as the ratio of block gas used and [KIP-71 MAX_BLOCK_GAS_USED_FOR_BASE_FEE](https://github.com/klaytn/kips/blob/main/KIPs/kip-71.md). If the ratio is above 1, then 1 is returned. | ||
- `reward` - (optional) A 2D array of effective priority fees per gas. `reward[n][i]` is the `rewardPercentiles[i]`-th percentile effective priority fees per gas among the transactions in the block `oldestBlock + n`. Only returned if `rewardPercentiles` is specified. | ||
- Example | ||
```sh | ||
curl $RPC_URL -X POST -H 'Content-Type: application/json' --data ' | ||
{"jsonrpc":"2.0","id":1,"method":"eth_feeHistory","params":[ | ||
4, | ||
"latest", | ||
[50.0, 90.0, 95.0] | ||
]}' | ||
|
||
{ | ||
"id": "1", | ||
"jsonrpc": "2.0", | ||
"result": { | ||
"oldestBlock": "0x8e0a025", | ||
"baseFeePerGas": ["0x5d21dba00", "0x5d21dba00", "0x5d21dba00", "0x61c9f3680"], | ||
"gasUsedRatio": [0.023, 0.31, 1.0, 0.88], | ||
"reward": [ | ||
["0x3b9aca00", "0x3b9aca00", "0x9502f900"], | ||
["0x3b9aca00", "0x3b9aca00", "0x9502f900"], | ||
["0x3b9aca00", "0x9502f900", "0x2e90edd00"], | ||
["0x3b9aca00", "0x9502f900", "0x30e4f9b40"], | ||
] | ||
} | ||
} | ||
``` | ||
|
||
# Rationale | ||
|
||
## A KIP-71 parameter is used in place of block gas limit | ||
|
||
The Ethereum's `eth_feeHistory` calculates gasUsedRatio as `block.gasUsed/blockGasLimit`. But Klaytn does not have hard limit of block gas. Instead, Klaytn's MAX_BLOCK_GAS_USED_FOR_BASE_FEE is analogous to Ethereum's block gas limit. | ||
|
||
In Ethereum, block gas limit is 30,000,000 and the base fee starts to rise when the block gas used exceeds 15,000,000 (`block.gasLimit / ELASTICITY_MULTIPLIER`). Similartly, the Klaytn's initial KIP-71 parameters stipulates that the block gas used is only accounted until 60,000,000 for the purpose of base fee calculation (`MAX_BLOCK_GAS_USED_FOR_BASE_FEE`), and the base fee starts to rise when the block gas used exceeds 30,000,000 (`GAS_TARGET`). | ||
|
||
# Backward compatibility | ||
|
||
## Continued use of other transaction types | ||
|
||
Legacy (type 0) transactions, EIP-2930 AccessList (type 1) transactions, and Klaytn transaction types (types 8+) will work and be included in blocks. Those transactions have `gasPrice` field but no `maxFeePerGas` nor `maxPriorityFeePerGas` fields. For those transaction types, their effective gas price is considered to equal to the `gasPrice`. The senders pay the full price as declared in the `gasPrice`. This policy is identical to [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) and the same as pre-1559 auction market. In this manner, those transaction types pay a nonzero priority fee. | ||
|
||
| baseFeePerGas | transaction | effectiveGasPrice | effectivePriorityFeePerGas | | ||
|-|-|-|-| | ||
| 25 | {type: 0, gasPrice: 26} | 26 | 1 | | ||
| 25 | {type: 2, maxFeePerGas: 51, maxPriorityFeePerGas: 1} | 26 | 1 | | ||
|
||
## EVM opcodes | ||
|
||
The `GASPRICE` (0x3a) opcode is backwards compatible because it had been correctly returning the effective gas price before `DRAGON_FORK_BLOCK_NUMBER`. The `BASEFEE` (0x48) opcode remains the same; returns the base fee per gas of the currently executing block. | ||
|
||
# References | ||
|
||
- https://github.com/klaytn/kips/blob/main/KIPs/kip-71.md | ||
- https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md | ||
- https://github.com/ethereum/execution-apis/blob/main/src/eth/fee_market.yaml |