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 10 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
75 changes: 43 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,46 @@ async def get_oracles() -> Oracles:
)


async def check_gas_price() -> bool:
max_fee_per_gas = await _get_max_fee_per_gas()
async def get_tx_params() -> TxParams:
evgeny-stakewise marked this conversation as resolved.
Show resolved Hide resolved
tx_params: TxParams = {}

max_priority_fee_per_gas = await _calc_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_priority_fee() -> Wei:
"""
reference: "high" priority value from https://etherscan.io/gastracker
"""
num_blocks = 10
evgeny-stakewise marked this conversation as resolved.
Show resolved Hide resolved
percentile = 80.0
history = await execution_client.eth.fee_history(num_blocks, 'pending', [percentile])
validator_rewards = [r[0] for r in history['reward']]
logger.info(
evgeny-stakewise marked this conversation as resolved.
Show resolved Hide resolved
'validator_rewards %s', sorted([round(Web3.from_wei(r, 'gwei')) for r in validator_rewards])
)
mean_reward = int(sum(validator_rewards) / len(validator_rewards))
return Wei(mean_reward)


async def check_gas_price(custom_priority_fee: bool = False) -> bool:
if custom_priority_fee:
# custom gas-price logic
tx_params = await get_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 +201,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()
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_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_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_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 @@ -133,7 +133,7 @@ async def register_validators(
)
return None

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

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