Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add priority fee #301

Merged
merged 14 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading