From 59bd7da8afb821fb61aca6b1c58ca4c1c806ef3c Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 12:49:07 +0530 Subject: [PATCH 01/13] fix: add strategy overrides --- run_service.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/run_service.py b/run_service.py index 4a92b26..9ba9226 100644 --- a/run_service.py +++ b/run_service.py @@ -29,6 +29,7 @@ from dataclasses import dataclass from pathlib import Path from decimal import Decimal, ROUND_UP +from enum import Enum import requests import yaml @@ -91,6 +92,11 @@ DEFAULT_MAX_FEE = 20000000 use_default_max_fee = True +class Strategy(Enum): + """Strategy type""" + MerklPoolSearchStrategy = "lp_strategy" + BalancerPoolSearchStrategy = "balancer_strategy" + def estimate_priority_fee( web3_object: Web3, block_number: int, @@ -146,6 +152,7 @@ class OptimusConfig(LocalResource): staking_chain: t.Optional[str] = None principal_chain: t.Optional[str] = None investment_funding_requirements: t.Optional[Dict[str, Any]] = None + selected_strategies: t.Optional[list[str]] = None @classmethod def from_json(cls, obj: t.Dict) -> "LocalResource": @@ -362,6 +369,9 @@ def configure_local_config() -> OptimusConfig: print() + if optimus_config.selected_strategies is None: + optimus_config.selected_strategies = [Strategy.MerklPoolSearchStrategy.value, Strategy.BalancerPoolSearchStrategy.value] + optimus_config.store() return optimus_config @@ -396,7 +406,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeiazaphqrn65tvscbubjvuh6mzmodqp3inwayjmye2jjweu3uea7wi", + "hash": "bafybeigy6gpuds4eu2khotxa2e6yibt4dyk54pds3psa7atug27h62t6vu", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", @@ -831,7 +841,8 @@ def main() -> None: "MIN_SWAP_AMOUNT_THRESHOLD": optimus_config.min_swap_amount_threshold, "ALLOWED_CHAINS": json.dumps(optimus_config.allowed_chains), "TARGET_INVESTMENT_CHAINS": json.dumps(optimus_config.target_investment_chains), - "INITIAL_ASSETS": json.dumps(initial_assets) + "INITIAL_ASSETS": json.dumps(initial_assets), + "SELECTED_STRATEGIES": json.dumps(optimus_config.selected_strategies) } apply_env_vars(env_vars) print("Skipping local deployment") From 1f37f64fe85447931f6fbd8ff98639123c1683b1 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 12:54:25 +0530 Subject: [PATCH 02/13] fix: hide secrets --- run_service.py | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/run_service.py b/run_service.py index 9ba9226..2f6c722 100644 --- a/run_service.py +++ b/run_service.py @@ -19,6 +19,9 @@ """Olas Modius Quickstart script.""" import warnings warnings.filterwarnings("ignore", category=UserWarning) +import sys +import tty +import termios import getpass import json import os @@ -134,6 +137,32 @@ def estimate_priority_fee( return values[len(values) // 2] +def get_masked_input(prompt: str) -> str: + """Get user input while masking it with asterisks.""" + password = "" + sys.stdout.write(prompt) + sys.stdout.flush() + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + try: + tty.setraw(fd) + while True: + char = sys.stdin.read(1) + if char == '\r' or char == '\n': + break + if char == '\x7f': + if password: + password = password[:-1] + sys.stdout.write('\b \b') + else: + password += char + sys.stdout.write('*') + sys.stdout.flush() + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.write('\n') + return password + @dataclass class OptimusConfig(LocalResource): """Local configuration.""" @@ -291,22 +320,22 @@ def configure_local_config() -> OptimusConfig: if optimus_config.tenderly_access_key is None: print_section("Tenderly API Configuration and Price Data Source") - optimus_config.tenderly_access_key = input( + optimus_config.tenderly_access_key = get_masked_input( "Please enter your Tenderly API Key. Get one at https://dashboard.tenderly.co/: " ) if optimus_config.tenderly_account_slug is None: - optimus_config.tenderly_account_slug = input( + optimus_config.tenderly_account_slug = get_masked_input( "Please enter your Tenderly Account Slug: " ) if optimus_config.tenderly_project_slug is None: - optimus_config.tenderly_project_slug = input( + optimus_config.tenderly_project_slug = get_masked_input( "Please enter your Tenderly Project Slug: " ) if optimus_config.coingecko_api_key is None: - optimus_config.coingecko_api_key = input( + optimus_config.coingecko_api_key = get_masked_input( "Please enter your CoinGecko API Key. Get one at https://www.coingecko.com/: " ) print() @@ -365,7 +394,7 @@ def configure_local_config() -> OptimusConfig: if optimus_config.mode_rpc is None: print_section("Chain RPC") - optimus_config.mode_rpc = input("Please enter a Mode RPC URL: ") + optimus_config.mode_rpc = get_masked_input("Please enter a Mode RPC URL: ") print() From 75f65c426ced73daa0f3a3fa72c23e54dda6dae8 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 12:57:03 +0530 Subject: [PATCH 03/13] fix: change gas requirements for operator --- run_service.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/run_service.py b/run_service.py index 2f6c722..aba3a3b 100644 --- a/run_service.py +++ b/run_service.py @@ -618,8 +618,12 @@ def fetch_agent_fund_requirement(chain_id, rpc, fee_history_blocks: int = 500000 return calculate_fund_requirement(rpc, fee_history_blocks, gas_amount) -def fetch_operator_fund_requirement(chain_id, rpc, fee_history_blocks: int = 500000) -> int: - gas_amount = 30_000_000 +def fetch_operator_fund_requirement(chain_id, rpc, service_exists: bool = True, fee_history_blocks: int = 500000) -> int: + if service_exists: + gas_amount = 5_000_000 + else: + gas_amount = 30_000_000 + if use_default_max_fee: return DEFAULT_MAX_FEE * gas_amount @@ -705,7 +709,7 @@ def main() -> None: if agent_fund_requirement is None: agent_fund_requirement = chain_config.chain_data.user_params.fund_requirements.agent - operational_fund_req = fetch_operator_fund_requirement(chain_id, chain_config.ledger_config.rpc) + operational_fund_req = fetch_operator_fund_requirement(chain_id, chain_config.ledger_config.rpc, service_exists) if operational_fund_req is None: operational_fund_req = chain_metadata.get("operationalFundReq") From c4d79a12cd001850f3130966b25f9fc72b5a765c Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 14:53:39 +0530 Subject: [PATCH 04/13] chore: update service hash --- run_service.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/run_service.py b/run_service.py index aba3a3b..2fdf2e0 100644 --- a/run_service.py +++ b/run_service.py @@ -97,8 +97,8 @@ class Strategy(Enum): """Strategy type""" - MerklPoolSearchStrategy = "lp_strategy" - BalancerPoolSearchStrategy = "balancer_strategy" + MerklPoolSearchStrategy = "merkl_pools_search" + BalancerPoolSearchStrategy = "balancer_pools_search" def estimate_priority_fee( web3_object: Web3, @@ -435,7 +435,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeigy6gpuds4eu2khotxa2e6yibt4dyk54pds3psa7atug27h62t6vu", + "hash": "bafybeibvdcz3j2bywodqap43suk5vkhhsa6s6ggvwif5mkp5adrnuxdjki", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", From d1ff96a2772485865126b2f24bf74d75da862a7e Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 17:21:57 +0530 Subject: [PATCH 05/13] fix: add olas balance --- report.py | 5 +++++ wallet_info.py | 21 +++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/report.py b/report.py index 6c47922..469cf14 100644 --- a/report.py +++ b/report.py @@ -142,6 +142,11 @@ def generate_report(): usdc_balance_formatted = safe_info.get('usdc_balance_formatted', 'N/A') _print_status("USDC Balance", usdc_balance_formatted) + # Check for OLAS balance on Staking chain + if chain_name.lower() == optimus_config.staking_chain: + olas_balance_formatted = safe_info.get('olas_balance_formatted', 'N/A') + _print_status("OLAS Balance", olas_balance_formatted) + # Low balance check safe_threshold_wei = chain_config.get("chain_data", {}).get("user_params", {}).get("fund_requirements", {}).get("safe") if safe_threshold_wei: diff --git a/wallet_info.py b/wallet_info.py index 808317d..21df78f 100644 --- a/wallet_info.py +++ b/wallet_info.py @@ -29,7 +29,7 @@ # Configure logging logging.basicConfig(level=logging.INFO, format='%(message)s') -USDC_ABI = [{ +TOKEN_ABI = [{ "constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf", @@ -37,6 +37,8 @@ "type": "function" }] +OLAS_ADDRESS = "0xcfD1D50ce23C46D3Cf6407487B2F8934e96DC8f9" + def load_config(): try: optimus_config = load_local_config() @@ -69,13 +71,22 @@ def get_balance(web3, address): def get_usdc_balance(web3, address, chain_name): try: - usdc_contract = web3.eth.contract(address=USDC_ADDRESS, abi=USDC_ABI) + usdc_contract = web3.eth.contract(address=USDC_ADDRESS, abi=TOKEN_ABI) balance = usdc_contract.functions.balanceOf(address).call() return Decimal(balance) / Decimal(1e6) # USDC has 6 decimal places except Exception as e: print(f"Error getting USDC balance for address {address}: {e}") return Decimal(0) +def get_olas_balance(web3, address, chain_name): + try: + olas_address = OLAS_ADDRESS[chain_name] + olas_contract = web3.eth.contract(address=OLAS_ADDRESS, abi=TOKEN_ABI) + balance = olas_contract.functions.balanceOf(address).call() + return Decimal(balance) / Decimal(1e18) # OLAS has 18 decimal places + except Exception as e: + print(f"Error getting OLAS balance for address {address}: {e}") + return Decimal(0) class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Decimal): @@ -148,6 +159,12 @@ def save_wallet_info(): safe_balances[chain_name]["usdc_balance"] = usdc_balance safe_balances[chain_name]["usdc_balance_formatted"] = f"{usdc_balance:.2f} USDC" + # Get USDC balance for Principal Chain + if chain_name.lower() == optimus_config.staking_chain: + olas_balance = get_olas_balance(web3, safe_address, chain_name.lower()) + safe_balances[chain_name]["olas_balance"] = usdc_balance + safe_balances[chain_name]["olas_balance_formatted"] = f"{olas_balance:.6f} OLAS" + except Exception as e: print(f"An error occurred while processing chain ID {chain_id}: {e}") continue From c8c98876922cd184481c83d4a922692e46d795bf Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 22:21:04 +0530 Subject: [PATCH 06/13] chore: update service hash --- run_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_service.py b/run_service.py index 2fdf2e0..6a09499 100644 --- a/run_service.py +++ b/run_service.py @@ -435,7 +435,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeibvdcz3j2bywodqap43suk5vkhhsa6s6ggvwif5mkp5adrnuxdjki", + "hash": "bafybeierrvod33ljm2lmuzmdc4bdyke57jlylpa3dwvnnbxsdu7z23f5um", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", From 28106ef96c001874c582ead9d877c37c37a78479 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 22:49:14 +0530 Subject: [PATCH 07/13] hotfix: remove extra volume --- operate/services/service.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/operate/services/service.py b/operate/services/service.py index c7d9ce5..365cd82 100644 --- a/operate/services/service.py +++ b/operate/services/service.py @@ -484,6 +484,16 @@ def _build_docker( # "SKILL_TRADER_ABCI_MODELS_PARAMS_ARGS_MECH_REQUEST_PRICE=10000000000000000" # noqa # ) # noqa + #temporary fix: remove extra volume + for service_name, service_data in deployment['services'].items(): + if 'abci' in service_name: + # Access the volumes list in this service + volumes = service_data.get('volumes', []) + + # Remove './data:/data:Z' if it's in the volumes list + if './data:/data:Z' in volumes: + volumes.remove('./data:/data:Z') + with (build / DOCKER_COMPOSE_YAML).open("w", encoding="utf-8") as stream: yaml_dump(data=deployment, stream=stream) From 9d4b70897f4e055caf6b6acad44ddc6114926d73 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Fri, 29 Nov 2024 22:53:51 +0530 Subject: [PATCH 08/13] fix: formatters --- operate/services/service.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/operate/services/service.py b/operate/services/service.py index 365cd82..3ab4d99 100644 --- a/operate/services/service.py +++ b/operate/services/service.py @@ -484,16 +484,16 @@ def _build_docker( # "SKILL_TRADER_ABCI_MODELS_PARAMS_ARGS_MECH_REQUEST_PRICE=10000000000000000" # noqa # ) # noqa - #temporary fix: remove extra volume - for service_name, service_data in deployment['services'].items(): - if 'abci' in service_name: + # temporary fix: remove extra volume + for service_name, service_data in deployment["services"].items(): + if "abci" in service_name: # Access the volumes list in this service - volumes = service_data.get('volumes', []) - + volumes = service_data.get("volumes", []) + # Remove './data:/data:Z' if it's in the volumes list - if './data:/data:Z' in volumes: - volumes.remove('./data:/data:Z') - + if "./data:/data:Z" in volumes: + volumes.remove("./data:/data:Z") + with (build / DOCKER_COMPOSE_YAML).open("w", encoding="utf-8") as stream: yaml_dump(data=deployment, stream=stream) From 52bd86b72e657d17ff2aca1a1a8ccc779a7adc74 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Tue, 3 Dec 2024 19:25:11 +0530 Subject: [PATCH 09/13] feat: add support for lending strategg --- run_service.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/run_service.py b/run_service.py index 6a09499..5223f6c 100644 --- a/run_service.py +++ b/run_service.py @@ -99,6 +99,7 @@ class Strategy(Enum): """Strategy type""" MerklPoolSearchStrategy = "merkl_pools_search" BalancerPoolSearchStrategy = "balancer_pools_search" + SturdyLendingStrategy = "asset_lending" def estimate_priority_fee( web3_object: Web3, @@ -400,6 +401,8 @@ def configure_local_config() -> OptimusConfig: if optimus_config.selected_strategies is None: optimus_config.selected_strategies = [Strategy.MerklPoolSearchStrategy.value, Strategy.BalancerPoolSearchStrategy.value] + if "mode" in optimus_config.target_investment_chains: + optimus_config.selected_strategies.append(Strategy.SturdyLendingStrategy.value) optimus_config.store() return optimus_config @@ -435,7 +438,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeierrvod33ljm2lmuzmdc4bdyke57jlylpa3dwvnnbxsdu7z23f5um", + "hash": "bafybeick34hcdbfcremtkre4dlgbkwdh2kqmwxbmigwd26t7gyrjpmdcqq", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", From 95df2def30edfc86a76e2472a502437cd27911ab Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Tue, 3 Dec 2024 20:30:49 +0530 Subject: [PATCH 10/13] chore: update service hash --- run_service.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/run_service.py b/run_service.py index 5223f6c..f33d3a9 100644 --- a/run_service.py +++ b/run_service.py @@ -400,9 +400,7 @@ def configure_local_config() -> OptimusConfig: print() if optimus_config.selected_strategies is None: - optimus_config.selected_strategies = [Strategy.MerklPoolSearchStrategy.value, Strategy.BalancerPoolSearchStrategy.value] - if "mode" in optimus_config.target_investment_chains: - optimus_config.selected_strategies.append(Strategy.SturdyLendingStrategy.value) + optimus_config.selected_strategies = [Strategy.MerklPoolSearchStrategy.value, Strategy.BalancerPoolSearchStrategy.value, Strategy.SturdyLendingStrategy.value] optimus_config.store() return optimus_config @@ -438,7 +436,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeick34hcdbfcremtkre4dlgbkwdh2kqmwxbmigwd26t7gyrjpmdcqq", + "hash": "bafybeicol5ukwibrtd2qsldjdcxtp6zvgiqwj7yk4pcdku4ocqgpuf2qry", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", From 6f2fb341405e9e21aafff4b61dad973453f2c1f2 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Tue, 3 Dec 2024 21:25:22 +0530 Subject: [PATCH 11/13] chore: update service hash --- run_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_service.py b/run_service.py index f33d3a9..694c6be 100644 --- a/run_service.py +++ b/run_service.py @@ -436,7 +436,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeicol5ukwibrtd2qsldjdcxtp6zvgiqwj7yk4pcdku4ocqgpuf2qry", + "hash": "bafybeib5amcj5lggqyo6uf7422uqzs7ozmmok4n7jaooggn7x526ihgi4a", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", From 3d287adf9fd8a194d79f305933b4592322e9649b Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Tue, 3 Dec 2024 21:53:30 +0530 Subject: [PATCH 12/13] chore: update service hash --- run_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_service.py b/run_service.py index 694c6be..38c745f 100644 --- a/run_service.py +++ b/run_service.py @@ -436,7 +436,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeib5amcj5lggqyo6uf7422uqzs7ozmmok4n7jaooggn7x526ihgi4a", + "hash": "bafybeieeex4vf4a3pj7ly563rxjrph5diylm2rr7qcvaks3ofqfvzxkjna", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", From 63689ca47c4baaeabb1e5e4bae0387b54a104cd0 Mon Sep 17 00:00:00 2001 From: Divya-Solulab Date: Tue, 10 Dec 2024 19:12:22 +0530 Subject: [PATCH 13/13] chore: update service hash --- run_service.py | 73 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/run_service.py b/run_service.py index 38c745f..d57dc84 100644 --- a/run_service.py +++ b/run_service.py @@ -44,7 +44,7 @@ from termcolor import colored from web3 import Web3 from web3.types import Wei, TxParams -from typing import Dict, Any +from typing import Dict, Any, Optional from operate.account.user import UserAccount from operate.cli import OperateApp @@ -436,7 +436,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: home_chain_id = "34443" return ServiceTemplate({ "name": "Optimus", - "hash": "bafybeieeex4vf4a3pj7ly563rxjrph5diylm2rr7qcvaks3ofqfvzxkjna", + "hash": "bafybeigp444eluxn77x5qdtuqok45gbhrg7l2ptdgntafldbay3x7bpsja", "description": "Optimus", "image": "https://gateway.autonolas.tech/ipfs/bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", @@ -450,7 +450,7 @@ def get_service_template(config: OptimusConfig) -> ServiceTemplate: "nft": "bafybeiaakdeconw7j5z76fgghfdjmsr6tzejotxcwnvmp3nroaw3glgyve", "cost_of_bond": COST_OF_BOND_STAKING if config.staking_chain == "mode" else COST_OF_BOND, "threshold": 1, - "use_staking": config.use_staking and config.staking_chain == "mode", ### + "use_staking": config.use_staking and config.staking_chain == "mode", "fund_requirements": FundRequirementsTemplate( { "agent": SUGGESTED_TOP_UP_DEFAULT * 5, @@ -591,26 +591,59 @@ def fetch_investing_funding_requirements(chain_name: str) -> None: optimus_config.store() -def calculate_fund_requirement(rpc, fee_history_blocks: int, gas_amount: int, fee_history_percentile: int = 50, priority_fee_increase_boundary: int = 200) -> int: - if rpc is None: +def calculate_fund_requirement( + rpc: str, + fee_history_blocks: int, + gas_amount: int, + fee_history_percentile: int = 50, + safety_margin: int = 500_000_000_000_000 +) -> Optional[int]: + """ + Calculate the estimated fund requirement for a transaction. + + Args: + rpc (str): RPC URL of the Ethereum node. + fee_history_blocks (int): Number of recent blocks for fee history. + gas_amount (int): Gas amount required for the transaction. + fee_history_percentile (int): Percentile for priority fee (default: 50). + safety_margin (int): Safety margin in wei (default: 500,000,000,000,000). + + Returns: + Optional[int]: Estimated fund requirement in wei, or None if unavailable. + """ + if not rpc: + print("RPC URL is required.") return None - - web3 = Web3(Web3.HTTPProvider(rpc)) - block_number = web3.eth.block_number - latest_block = web3.eth.get_block("latest") - base_fee = latest_block.get("baseFeePerGas") - - if base_fee is None or block_number is None: + + try: + web3 = Web3(Web3.HTTPProvider(rpc)) + block_number = web3.eth.block_number + fee_history = web3.eth.fee_history( + fee_history_blocks, block_number, [fee_history_percentile] + ) + except Exception as e: return None - - priority_fee = estimate_priority_fee(web3, block_number, fee_history_blocks, fee_history_percentile, priority_fee_increase_boundary) - if priority_fee is None: + + if not fee_history or 'baseFeePerGas' not in fee_history: + return None + + base_fees = fee_history.get('baseFeePerGas', []) + priority_fees = [reward[0] for reward in fee_history.get('reward', []) if reward] + + if not base_fees or not priority_fees: + return None + + try: + # Calculate averages + average_base_fee = sum(base_fees) / len(base_fees) + average_priority_fee = sum(priority_fees) / len(priority_fees) + average_gas_price = average_base_fee + average_priority_fee + + # Calculate fund requirement + fund_requirement = int((average_gas_price * gas_amount) + safety_margin) + return fund_requirement + except Exception as e: return None - - gas_price = base_fee + priority_fee - safety_margin = 500_000_000_000_000 - fund_requirement = int((gas_price * gas_amount) + safety_margin) - return fund_requirement def fetch_agent_fund_requirement(chain_id, rpc, fee_history_blocks: int = 500000) -> int: gas_amount = 50_000_000