Skip to content

Commit

Permalink
Add priority fee (#301)
Browse files Browse the repository at this point in the history
* Fix tx confirm

* Add priority fee setting

* Del custom priority fee from harvest tx

* Fix custom_priority_fee

* Add max-priority-fee to network config

* Revert "Add max-priority-fee to network config"

This reverts commit 141d349.

* Del max_priority_fee_per_gas_gwei setting

* Del _calculate_median_priority_fee

* Review fixes

* Add fee settings

* Add rounding
  • Loading branch information
evgeny-stakewise authored Mar 18, 2024
1 parent 859ebc7 commit 2173706
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src/commands/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
'--max-fee-per-gas-gwei',
type=int,
envvar='MAX_FEE_PER_GAS_GWEI',
help=f'Maximum fee per gas limit for transactions. '
help=f'Maximum fee per gas for transactions. '
f'Default is {DEFAULT_MAX_FEE_PER_GAS_GWEI} Gwei.',
default=DEFAULT_MAX_FEE_PER_GAS_GWEI,
)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/start_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
'--max-fee-per-gas-gwei',
type=int,
envvar='MAX_FEE_PER_GAS_GWEI',
help=f'Maximum fee per gas limit for transactions. '
help=f'Maximum fee per gas for transactions. '
f'Default is {DEFAULT_MAX_FEE_PER_GAS_GWEI} Gwei.',
default=DEFAULT_MAX_FEE_PER_GAS_GWEI,
)
Expand Down
81 changes: 49 additions & 32 deletions src/common/execution.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import logging
import statistics
from typing import cast

import click
from eth_typing import BlockNumber
from web3 import Web3
from web3.exceptions import BadFunctionCallOutput, MethodUnavailable
from web3.types import BlockIdentifier, Wei
from web3._utils.async_transactions import _max_fee_per_gas
from web3.exceptions import BadFunctionCallOutput
from web3.types import TxParams, Wei

from src.common.clients import execution_client, ipfs_fetch_client
from src.common.contracts import keeper_contract, multicall_contract, vault_contract
Expand Down Expand Up @@ -149,8 +149,52 @@ async def get_oracles() -> Oracles:
)


async def check_gas_price() -> bool:
max_fee_per_gas = await _get_max_fee_per_gas()
async def get_high_priority_tx_params() -> TxParams:
"""
`maxPriorityFeePerGas <= maxFeePerGas` must be fulfilled
Because of that when increasing `maxPriorityFeePerGas` I have to adjust `maxFeePerGas`.
See https://eips.ethereum.org/EIPS/eip-1559 for details.
"""
tx_params: TxParams = {}

max_priority_fee_per_gas = await _calc_high_priority_fee()

# Reference: `_max_fee_per_gas` in web3/_utils/async_transactions.py
block = await execution_client.eth.get_block('latest')
max_fee_per_gas = Wei(max_priority_fee_per_gas + (2 * block['baseFeePerGas']))

tx_params['maxPriorityFeePerGas'] = max_priority_fee_per_gas
tx_params['maxFeePerGas'] = max_fee_per_gas
logger.debug('tx_params %s', tx_params)

return tx_params


async def _calc_high_priority_fee() -> Wei:
"""
reference: "high" priority value from https://etherscan.io/gastracker
"""
num_blocks = settings.priority_fee_num_blocks
percentile = settings.priority_fee_percentile
history = await execution_client.eth.fee_history(num_blocks, 'pending', [percentile])
validator_rewards = [r[0] for r in history['reward']]
mean_reward = int(sum(validator_rewards) / len(validator_rewards))

# prettify `mean_reward`
# same as `round(value, 1)` if value was in gwei
mean_reward = round(mean_reward, -8)

return Wei(mean_reward)


async def check_gas_price(high_priority: bool = False) -> bool:
if high_priority:
tx_params = await get_high_priority_tx_params()
max_fee_per_gas = Wei(int(tx_params['maxFeePerGas']))
else:
# fallback to logic from web3
max_fee_per_gas = await _max_fee_per_gas(execution_client, {})

if max_fee_per_gas >= Web3.to_wei(settings.max_fee_per_gas_gwei, 'gwei'):
logging.warning(
'Current gas price (%s gwei) is too high. '
Expand All @@ -163,33 +207,6 @@ async def check_gas_price() -> bool:
return True


async def _get_max_fee_per_gas() -> Wei:
try:
priority_fee = await execution_client.eth.max_priority_fee
except MethodUnavailable:
priority_fee = await _calculate_median_priority_fee()
latest_block = await execution_client.eth.get_block('latest')
base_fee = latest_block['baseFeePerGas']
max_fee_per_gas = priority_fee + base_fee
return Wei(max_fee_per_gas)


async def _calculate_median_priority_fee(block_id: BlockIdentifier = 'latest') -> Wei:
block = await execution_client.eth.get_block(block_id)

# collect maxPriorityFeePerGas for all transactions in the block
priority_fees = []
for tx_hash in block.transactions: # type: ignore[attr-defined]
tx = await execution_client.eth.get_transaction(tx_hash)
if 'maxPriorityFeePerGas' in tx:
priority_fees.append(tx.maxPriorityFeePerGas) # type: ignore[attr-defined]

if not priority_fees:
return await _calculate_median_priority_fee(block['number'] - 1)

return Wei(statistics.median(priority_fees))


class WalletTask(BaseTask):
async def process_block(self) -> None:
await check_hot_wallet_balance()
6 changes: 6 additions & 0 deletions src/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ class Settings(metaclass=Singleton):
validators_registration_mode: ValidatorsRegistrationMode
skip_startup_checks: bool

# high priority fee
priority_fee_num_blocks: int = decouple_config('PRIORITY_FEE_NUM_BLOCKS', default=10, cast=int)
priority_fee_percentile: float = decouple_config(
'PRIORITY_FEE_PERCENTILE', default=80.0, cast=float
)

# pylint: disable-next=too-many-arguments,too-many-locals
def set(
self,
Expand Down
15 changes: 11 additions & 4 deletions src/validators/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
validators_registry_contract,
vault_contract,
)
from src.common.execution import get_high_priority_tx_params
from src.common.ipfs import fetch_harvest_params
from src.common.metrics import metrics
from src.common.typings import OraclesApproval
Expand Down Expand Up @@ -249,16 +250,19 @@ async def register_single_validator(
multi_proof.proof,
]
try:
tx_params = await get_high_priority_tx_params()

if update_state_call is not None:
register_call = vault_contract.encode_abi(
fn_name='registerValidator',
args=register_call_args,
)
tx = await vault_contract.functions.multicall(
[update_state_call, register_call]
).transact()
).transact(tx_params)
else:
tx = await vault_contract.functions.registerValidator(*register_call_args).transact()
register_func = vault_contract.functions.registerValidator
tx = await register_func(*register_call_args).transact(tx_params)
except Exception as e:
logger.error('Failed to register validator: %s', format_error(e))
if settings.verbose:
Expand Down Expand Up @@ -305,16 +309,19 @@ async def register_multiple_validator(
multi_proof.proof,
]
try:
tx_params = await get_high_priority_tx_params()

if update_state_call is not None:
register_call = vault_contract.encode_abi(
fn_name='registerValidators',
args=register_call_args,
)
tx = await vault_contract.functions.multicall(
[update_state_call, register_call]
).transact()
).transact(tx_params)
else:
tx = await vault_contract.functions.registerValidators(*register_call_args).transact()
register_func = vault_contract.functions.registerValidators
tx = await register_func(*register_call_args).transact(tx_params)
except Exception as e:
logger.error('Failed to register validators: %s', format_error(e))
if settings.verbose:
Expand Down
2 changes: 1 addition & 1 deletion src/validators/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ async def register_validators(
)
return None

if not await check_gas_price():
if not await check_gas_price(high_priority=True):
return None

logger.info('Started registration of %d validator(s)', len(validators))
Expand Down

0 comments on commit 2173706

Please sign in to comment.