diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index a7a6b3cd8..08e8c02cd 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -55,7 +55,6 @@ from typing import Generator, List, Dict, Tuple, Any, Callable from typing import Optional -import fastlane_bot from fastlane_bot.config import Config from fastlane_bot.helpers import ( TxRouteHandler, @@ -63,11 +62,10 @@ TradeInstruction, Univ3Calculator, add_wrap_or_unwrap_trades_to_route, - split_carbon_trades + split_carbon_trades, + maximize_last_trade_per_tkn ) -from fastlane_bot.helpers.routehandler import maximize_last_trade_per_tkn from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer, T -from fastlane_bot.tools.optimizer import CPCArbOptimizer from .config.constants import FLASHLOAN_FEE_MAP from .events.interface import QueryInterface from .modes.pairwise_multi import FindArbitrageMultiPairwise @@ -77,35 +75,43 @@ from .modes.triangle_multi import ArbitrageFinderTriangleMulti from .modes.triangle_single import ArbitrageFinderTriangleSingle from .modes.triangle_bancor_v3_two_hop import ArbitrageFinderTriangleBancor3TwoHop -from .utils import num_format, log_format, num_format_float +from .utils import num_format @dataclass -class CarbonBotBase: +class CarbonBot: """ Base class for the business logic of the bot. Attributes ---------- - db: DatabaseManager + db: QueryInterface the database manager. - TxRouteHandlerClass - ditto (default: TxRouteHandler). - TxHelpersClass: - ditto (default: TxHelpers). - + tx_helpers: TxHelpers + the tx-helpers utility. """ __VERSION__ = __VERSION__ __DATE__ = __DATE__ db: QueryInterface = field(init=False) - TxRouteHandlerClass: any = None - TxHelpersClass: any = None + tx_helpers: TxHelpers = None ConfigObj: Config = None - usd_gas_limit: int = 150 - min_profit: int = 60 - polling_interval: int = None + + SCALING_FACTOR = 0.999 + + ARB_FINDER = { + "single": FindArbitrageSinglePairwise, + "multi": FindArbitrageMultiPairwise, + "triangle": ArbitrageFinderTriangleSingle, + "multi_triangle": ArbitrageFinderTriangleMulti, + "b3_two_hop": ArbitrageFinderTriangleBancor3TwoHop, + "multi_pairwise_pol": FindArbitrageMultiPairwisePol, + "multi_pairwise_all": FindArbitrageMultiPairwiseAll, + } + + class NoArbAvailable(Exception): + pass def __post_init__(self): """ @@ -115,39 +121,12 @@ def __post_init__(self): if self.ConfigObj is None: self.ConfigObj = Config() - assert ( - self.polling_interval is None - ), "polling_interval is now a parameter to run" - - if self.TxRouteHandlerClass is None: - self.TxRouteHandlerClass = TxRouteHandler - - if self.TxHelpersClass is None: - self.TxHelpersClass = TxHelpers(cfg=self.ConfigObj) + if self.tx_helpers is None: + self.tx_helpers = TxHelpers(cfg=self.ConfigObj) self.db = QueryInterface(ConfigObj=self.ConfigObj) self.RUN_FLASHLOAN_TOKENS = [*self.ConfigObj.CHAIN_FLASHLOAN_TOKENS.values()] - @property - def C(self) -> Any: - """ - Convenience method self.ConfigObj - """ - return self.ConfigObj - - @staticmethod - def versions(): - """ - Returns the versions of the module and its Carbon dependencies. - """ - s = [f"fastlane_bot v{__VERSION__} ({__DATE__})"] - s += ["carbon v{0.__VERSION__} ({0.__DATE__})".format(CPC)] - s += ["carbon v{0.__VERSION__} ({0.__DATE__})".format(CPCArbOptimizer)] - return s - - UDTYPE_FROM_CONTRACTS = "from_contracts" - UDTYPE_FROM_EVENTS = "from_events" - def get_curves(self) -> CPCContainer: """ Gets the curves from the database. @@ -171,13 +150,9 @@ def get_curves(self) -> CPCContainer: if all(curve.params[tkn] not in self.ConfigObj.TAX_TOKENS for tkn in ['tknx_addr', 'tkny_addr']) ] except NotImplementedError as e: - # Currently not supporting Solidly V2 Stable pools. This will be removed when support is added, but for now the error message is suppressed. - if "Stable Solidly V2" in str(e): - continue - else: - self.ConfigObj.logger.error( - f"[bot.get_curves] Pool type not yet supported, error: {e}\n" - ) + self.ConfigObj.logger.error( + f"[bot.get_curves] Pool type not yet supported, error: {e}\n" + ) except ZeroDivisionError as e: self.ConfigObj.logger.error( f"[bot.get_curves] MUST FIX INVALID CURVE {p} [{e}]\n" @@ -187,10 +162,9 @@ def get_curves(self) -> CPCContainer: f"[bot.get_curves] MUST FIX INVALID CURVE {p} [{e}]\n" ) except TypeError as e: - if fastlane_bot.__version__ not in ["3.0.31", "3.0.32"]: - self.ConfigObj.logger.error( - f"[bot.get_curves] MUST FIX DECIMAL ERROR CURVE {p} [{e}]\n" - ) + self.ConfigObj.logger.error( + f"[bot.get_curves] MUST FIX DECIMAL ERROR CURVE {p} [{e}]\n" + ) except p.DoubleInvalidCurveError as e: self.ConfigObj.logger.error( f"[bot.get_curves] MUST FIX DOUBLE INVALID CURVE {p} [{e}]\n" @@ -200,45 +174,13 @@ def get_curves(self) -> CPCContainer: f"[bot.get_curves] MUST FIX DECIMALS MISSING [{e}]\n" ) except Exception as e: - # TODO: unexpected Exception should possible raise + # TODO: unexpected exception should possibly be raised self.ConfigObj.logger.error( f"[bot.get_curves] MUST FIX UNEXPECTED ERROR converting pool to curve {p}\n[ERR={e}]\n\n" ) return CPCContainer(curves) - -@dataclass -class CarbonBot(CarbonBotBase): - """ - A class that handles the business logic of the bot. - - MAIN ENTRY POINTS - :run: Runs the bot. - """ - - RUN_POLLING_INTERVAL = 60 # default polling interval in seconds - SCALING_FACTOR = 0.999 - AO_TOKENS = "tokens" - AO_CANDIDATES = "candidates" - BNT_ETH_CID = "0xc4771395e1389e2e3a12ec22efbb7aff5b1c04e5ce9c7596a82e9dc8fdec725b" - - ARB_FINDER = { - "single": FindArbitrageSinglePairwise, - "multi": FindArbitrageMultiPairwise, - "triangle": ArbitrageFinderTriangleSingle, - "multi_triangle": ArbitrageFinderTriangleMulti, - "b3_two_hop": ArbitrageFinderTriangleBancor3TwoHop, - "multi_pairwise_pol": FindArbitrageMultiPairwisePol, - "multi_pairwise_all": FindArbitrageMultiPairwiseAll, - } - - def __post_init__(self): - super().__post_init__() - - class NoArbAvailable(Exception): - pass - def _simple_ordering_by_src_token( self, best_trade_instructions_dic, best_src_token ): @@ -325,20 +267,6 @@ def _add_strategy_id_to_trade_instructions_dic( lst.append(ti) return lst - @dataclass - class ArbCandidate: - """ - The arbitrage candidates. - """ - - result: any - constains_carbon: bool = None - best_profit_usd: float = None - - @property - def r(self): - return self.result - def _get_deadline(self, block_number) -> int: """ Gets the deadline for a transaction. @@ -367,15 +295,16 @@ def _find_arbitrage( arb_mode: str, randomizer: int ) -> dict: - random_mode = self.AO_CANDIDATES if randomizer else None - arb_finder = self._get_arb_finder(arb_mode)( + arb_finder = self._get_arb_finder(arb_mode) + random_mode = arb_finder.AO_CANDIDATES if randomizer else None + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", result=random_mode, ConfigObj=self.ConfigObj, ) - return {"finder": arb_finder, "r": arb_finder.find_arbitrage()} + return {"finder": finder, "r": finder.find_arbitrage()} def _run( self, @@ -385,7 +314,9 @@ def _run( arb_mode: str, randomizer: int, data_validator=False, + logging_path: str = None, replay_mode: bool = False, + replay_from_block: int = None, ): """ Runs the bot. @@ -402,6 +333,12 @@ def _run( randomizer (int): The number of arb opportunities to randomly pick from, sorted by expected profit. data_validator: bool If extra data validation should be performed + logging_path: str + the logging path (default: None) + replay_mode: bool + whether to run in replay mode (default: False) + replay_from_block: int + the block number to start replaying from (default: None) """ arbitrage = self._find_arbitrage(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode=arb_mode, randomizer=randomizer) @@ -435,16 +372,16 @@ def _run( ) return - tx_hash, tx_receipt = self._handle_trade_instructions(CCm, arb_mode, r) + tx_hash, tx_receipt = self._handle_trade_instructions(CCm, arb_mode, r, replay_from_block) if tx_hash: tx_status = ["failed", "succeeded"][tx_receipt["status"]] if tx_receipt else "pending" tx_details = json.dumps(tx_receipt, indent=4) if tx_receipt else "no receipt" self.ConfigObj.logger.info(f"Arbitrage transaction {tx_hash} {tx_status}") - if self.logging_path: + if logging_path: filename = f"tx_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt" - with open(os.path.join(self.logging_path, filename), "w") as f: + with open(os.path.join(logging_path, filename), "w") as f: f.write(f"{tx_hash} {tx_status}: {tx_details}") def validate_optimizer_trades(self, arb_opp, arb_finder): @@ -559,16 +496,8 @@ def validate_pool_data(self, arb_opp): } fetched_pool = self.db.mgr.update_from_pool_info(pool_info=pool_info) - if fetched_pool is None: - self.ConfigObj.logger.error( - f"[bot.validate_pool_data] Could not fetch pool data for {pool_cid}" - ) - - ex_name = fetched_pool["exchange_name"] - self._validate_pool_data_logging(pool_cid, fetched_pool) - - if ex_name == "bancor_v3": - self._validate_pool_data_logging(pool_cid, fetched_pool) + self.ConfigObj.logger.debug(f"[bot.validate_pool_data] pool_cid: {pool_cid}") + self.ConfigObj.logger.debug(f"[bot.validate_pool_data] fetched_pool: {fetched_pool}") if current_pool.exchange_name in self.ConfigObj.CARBON_V1_FORKS: if ( @@ -604,7 +533,7 @@ def validate_pool_data(self, arb_opp): or current_pool.tkn1_balance != fetched_pool["tkn1_balance"] ): self.ConfigObj.logger.debug( - f"[bot.validate_pool_data] {ex_name} pool not up to date, updating and restarting." + f"[bot.validate_pool_data] {fetched_pool['exchange_name']} pool not up to date, updating and restarting." ) return False @@ -620,35 +549,10 @@ def randomize(arb_opps, randomizer: int = 1): A randomly selected arb opportunity. """ - if arb_opps is None: - return None - if len(arb_opps) > 0: - arb_opps.sort(key=lambda x: x[0], reverse=True) - randomizer = min(max(randomizer, 1), len(arb_opps)) - top_n_arbs = arb_opps[:randomizer] - return random.choice(top_n_arbs) - else: - return None - - def _validate_pool_data_logging( - self, pool_cid: str, fetched_pool: Dict[str, Any] - ) -> None: - """ - Logs the pool data validation. - - Parameters - ---------- - pool_cid: str - The pool CID. - fetched_pool: dict - The fetched pool data. - - """ - self.ConfigObj.logger.debug(f"[bot.py validate] pool_cid: {pool_cid}") - self.ConfigObj.logger.debug( - f"[bot.py validate] fetched_pool: {fetched_pool['exchange_name']}" - ) - self.ConfigObj.logger.debug(f"[bot.py validate] fetched_pool: {fetched_pool}") + arb_opps.sort(key=lambda x: x[0], reverse=True) + randomizer = min(max(randomizer, 1), len(arb_opps)) + top_n_arbs = arb_opps[:randomizer] + return random.choice(top_n_arbs) @staticmethod def _carbon_in_trade_route(trade_instructions: List[TradeInstruction]) -> bool: @@ -709,7 +613,7 @@ def calculate_profit( Tuple[Decimal, Decimal, Decimal] The updated best_profit, flt_per_bnt, and profit_usd. """ - self.ConfigObj.logger.debug(f"[bot.calculate_profit_usd] best_profit, fl_token, flashloan_fee_amt: {best_profit, fl_token, flashloan_fee_amt}") + self.ConfigObj.logger.debug(f"[bot.calculate_profit] best_profit, fl_token, flashloan_fee_amt: {best_profit, fl_token, flashloan_fee_amt}") sort_sequence = ['bancor_v2','bancor_v3'] + self.ConfigObj.UNI_V2_FORKS + self.ConfigObj.UNI_V3_FORKS best_profit_fl_token = best_profit @@ -743,20 +647,20 @@ def calculate_profit( usd_gastkn_conversion_rate = Decimal("NaN") best_profit_usd = best_profit_gastkn * usd_gastkn_conversion_rate - self.ConfigObj.logger.debug(f"[bot.calculate_profit_usd] {'GASTOKEN', best_profit_gastkn, usd_gastkn_conversion_rate, best_profit_usd, 'USD'}") + self.ConfigObj.logger.debug(f"[bot.calculate_profit] {'GASTOKEN', best_profit_gastkn, usd_gastkn_conversion_rate, best_profit_usd, 'USD'}") return best_profit_fl_token, best_profit_gastkn, best_profit_usd @staticmethod - def update_log_dict( + def calculate_arb( arb_mode: str, best_profit_gastkn: Decimal, best_profit_usd: Decimal, flashloan_tkn_profit: Decimal, calculated_trade_instructions: List[Any], fl_token: str, - ) -> Dict[str, Any]: + ) -> dict: """ - Update the log dictionary. + Calculate the arbitrage. Parameters ---------- @@ -776,45 +680,40 @@ def update_log_dict( Returns ------- dict - The updated log dictionary. + The arbitrage. """ - flashloans = [ - { - "token": fl_token, - "amount": num_format_float(calculated_trade_instructions[0].amtin), - "profit": num_format_float(flashloan_tkn_profit), - } - ] - log_dict = { - "type": arb_mode, - "profit_gas_token": num_format_float(best_profit_gastkn), - "profit_usd": num_format_float(best_profit_usd), - "flashloan": flashloans, - "trades": [], - } - for idx, trade in enumerate(calculated_trade_instructions): - tknin = {trade.tknin_symbol: trade.tknin} if trade.tknin_symbol != trade.tknin else trade.tknin - tknout = {trade.tknout_symbol: trade.tknout} if trade.tknout_symbol != trade.tknout else trade.tknout - log_dict["trades"].append( + return { + "type": arb_mode, + "profit_gas_token": num_format(best_profit_gastkn), + "profit_usd": num_format(best_profit_usd), + "flashloan": [ + { + "token": fl_token, + "amount": num_format(calculated_trade_instructions[0].amtin), + "profit": num_format(flashloan_tkn_profit) + } + ], + "trades": [ { "trade_index": idx, "exchange": trade.exchange_name, - "tkn_in": tknin, - "amount_in": num_format_float(trade.amtin), - "tkn_out": tknout, - "amt_out": num_format_float(trade.amtout), - "cid0": trade.cid[-10:], + "tkn_in": {trade.tknin_symbol: trade.tknin} if trade.tknin_symbol != trade.tknin else trade.tknin, + "amount_in": num_format(trade.amtin), + "tkn_out": {trade.tknout_symbol: trade.tknout} if trade.tknout_symbol != trade.tknout else trade.tknout, + "amt_out": num_format(trade.amtout), + "cid0": trade.cid[-10:] } - ) - - return log_dict + for idx, trade in enumerate(calculated_trade_instructions) + ] + } def _handle_trade_instructions( self, CCm: CPCContainer, arb_mode: str, - r: Any + r: Any, + replay_from_block: int = None ) -> Tuple[Optional[str], Optional[dict]]: """ Creates and executes the trade instructions @@ -830,6 +729,8 @@ def _handle_trade_instructions( The arbitrage mode. r: Any The result. + replay_from_block: int + the block number to start replaying from (default: None) Returns ------- @@ -863,7 +764,7 @@ def _handle_trade_instructions( ) # Create the tx route handler - tx_route_handler = self.TxRouteHandlerClass( + tx_route_handler = TxRouteHandler( trade_instructions=ordered_trade_instructions_objects ) @@ -900,18 +801,18 @@ def _handle_trade_instructions( calculated_trade_instructions ) - # Use helper function to calculate profit + # Calculate the best profit best_profit_fl_token, best_profit_gastkn, best_profit_usd = self.calculate_profit( CCm, best_profit, fl_token, flashloan_fee_amt ) - # Log the best trade instructions - self.handle_logging_for_trade_instructions( - 1, best_profit=best_profit_gastkn # The log id + # Log the best profit + self.ConfigObj.logger.debug( + f"[bot._handle_trade_instructions] Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}" ) - # Use helper function to update the log dict - log_dict = self.update_log_dict( + # Calculate the arbitrage + arb = self.calculate_arb( arb_mode, best_profit_gastkn, best_profit_usd, @@ -920,8 +821,10 @@ def _handle_trade_instructions( fl_token_symbol, ) - # Log the log dict - self.handle_logging_for_trade_instructions(2, log_dict=log_dict) # The log id + # Log the arbitrage + self.ConfigObj.logger.info( + f"[bot._handle_trade_instructions] calculated arb: {arb}" + ) # Check if the best profit is greater than the minimum profit if best_profit_gastkn < self.ConfigObj.DEFAULT_MIN_PROFIT_GAS_TOKEN: @@ -930,13 +833,9 @@ def _handle_trade_instructions( ) return None, None - # Get the flashloan amount and token address - flashloan_token_address = fl_token - - # Log the flashloan amount and token address - self.handle_logging_for_trade_instructions( - 3, # The log id - flashloan_amount=flashloan_amount_wei, + # Log the flashloan amount + self.ConfigObj.logger.debug( + f"[bot._handle_trade_instructions] Flashloan amount: {flashloan_amount_wei}" ) # Split Carbon Orders @@ -951,7 +850,7 @@ def _handle_trade_instructions( ) # Get the deadline - deadline = self._get_deadline(self.replay_from_block) + deadline = self._get_deadline(replay_from_block) # Get the route struct route_struct = [ @@ -968,168 +867,32 @@ def _handle_trade_instructions( trade_instructions=split_calculated_trade_instructions, ) - route_struct_maximized = maximize_last_trade_per_tkn(route_struct=route_struct_processed) - - # Log the route_struct - self.handle_logging_for_trade_instructions( - 4, # The log id - flashloan_amount=flashloan_amount_wei, - flashloan_token_symbol=fl_token_symbol, - flashloan_token_address=flashloan_token_address, - route_struct=route_struct_maximized, - best_trade_instructions_dic=best_trade_instructions_dic, - ) - - # Validate and submit the transaction - return self.TxHelpersClass.validate_and_submit_transaction( - route_struct=route_struct_maximized, - src_amt=flashloan_amount_wei, - src_address=flashloan_token_address, - expected_profit_gastkn=best_profit_gastkn, - expected_profit_usd=best_profit_usd, - flashloan_struct=flashloan_struct, - ) - - def handle_logging_for_trade_instructions(self, log_id: int, **kwargs): - """ - Handles logging for trade instructions based on log_id. - - Parameters - ---------- - log_id : int - The ID for log type. - **kwargs : dict - Additional parameters required for logging. - - Returns - ------- - None - """ - log_actions = { - 1: self.log_best_profit, - 2: self.log_calculated_arb, - 3: self.log_flashloan_amount, - 4: self.log_flashloan_details, - } - log_action = log_actions.get(log_id) - if log_action: - log_action(**kwargs) - - def log_best_profit(self, best_profit: Optional[float] = None): - """ - Logs the best profit. - - Parameters - ---------- - best_profit : Optional[float], optional - The best profit, by default None - """ - self.ConfigObj.logger.debug( - f"[bot.log_best_profit] Updated best_profit after calculating exact trade numbers: {num_format(best_profit)}" - ) - - def log_calculated_arb(self, log_dict: Optional[Dict] = None): - """ - Logs the calculated arbitrage. - - Parameters - ---------- - log_dict : Optional[Dict], optional - The dictionary containing log data, by default None - """ - self.ConfigObj.logger.info( - f"[bot.log_calculated_arb] {log_format(log_data=log_dict, log_name='calculated_arb')}" - ) - - def log_flashloan_amount(self, flashloan_amount: Optional[float] = None): - """ - Logs the flashloan amount. + maximize_last_trade_per_tkn(route_struct=route_struct_processed) - Parameters - ---------- - flashloan_amount : Optional[float], optional - The flashloan amount, by default None - """ + # Log the flashloan details self.ConfigObj.logger.debug( - f"[bot.log_flashloan_amount] Flashloan amount: {flashloan_amount}" - ) - - def log_flashloan_details( - self, - flashloan_amount: Optional[float] = None, - flashloan_token_address: Optional[str] = None, - flashloan_token_symbol: Optional[str] = None, - route_struct: Optional[List[Dict]] = None, - best_trade_instructions_dic: Optional[Dict] = None, - ): - """ - Logs the details of flashloan. - - Parameters - ---------- - flashloan_amount : Optional[float], optional - The flashloan amount, by default None - flashloan_token_symbol : Optional[str], optional - The flashloan token symbol, by default None - flashloan_token_address : Optional[str], optional - The flashloan token address, by default None - route_struct : Optional[List[Dict]], optional - The route structure, by default None - best_trade_instructions_dic : Optional[Dict], optional - The dictionary containing the best trade instructions, by default None - """ - self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Flashloan of {flashloan_token_symbol}, amount: {flashloan_amount}" + f"[bot._handle_trade_instructions] Flashloan of {fl_token_symbol}, amount: {flashloan_amount_wei}" ) self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Flashloan token address: {flashloan_token_address}" + f"[bot._handle_trade_instructions] Flashloan token address: {fl_token}" ) self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Route Struct: \n {route_struct}" + f"[bot._handle_trade_instructions] Route Struct: \n {route_struct_processed}" ) self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Trade Instructions: \n {best_trade_instructions_dic}" + f"[bot._handle_trade_instructions] Trade Instructions: \n {best_trade_instructions_dic}" ) - def setup_polling_interval(self, polling_interval: int): - """ - Setup the polling interval. If the polling interval is None, set it to RUN_POLLING_INTERVAL. - """ - if self.polling_interval is None: - self.polling_interval = ( - polling_interval - if polling_interval is not None - else self.RUN_POLLING_INTERVAL - ) - - def setup_flashloan_tokens(self, flashloan_tokens): - """ - Setup the flashloan tokens. If flashloan_tokens is None, set it to RUN_FLASHLOAN_TOKENS. - """ - return ( - flashloan_tokens - if flashloan_tokens is not None - else self.RUN_FLASHLOAN_TOKENS + # Validate and submit the transaction + return self.tx_helpers.validate_and_submit_transaction( + route_struct=route_struct_processed, + src_amt=flashloan_amount_wei, + src_address=fl_token, + expected_profit_gastkn=best_profit_gastkn, + expected_profit_usd=best_profit_usd, + flashloan_struct=flashloan_struct, ) - def setup_CCm(self, CCm: CPCContainer) -> CPCContainer: - """ - Setup the CCm. If CCm is None, retrieve and filter curves. - - Parameters - ---------- - CCm: CPCContainer - The CPCContainer object - - Returns - ------- - CPCContainer - The filtered CPCContainer object - """ - if CCm is None: - CCm = self.get_curves() - return CCm - def get_tokens_in_exchange( self, exchange_name: str, @@ -1145,7 +908,6 @@ def run( *, flashloan_tokens: List[str] = None, CCm: CPCContainer = None, - polling_interval: int = None, arb_mode: str = None, run_data_validator: bool = False, randomizer: int = 0, @@ -1162,8 +924,6 @@ def run( The flashloan tokens (optional; default: RUN_FLASHLOAN_TOKENS) CCm: CPCContainer The complete market data container (optional; default: database via get_curves()) - polling_interval: int - the polling interval in seconds (default: 60 via RUN_POLLING_INTERVAL) arb_mode: str the arbitrage mode (default: None; can be set depending on arbmode) run_data_validator: bool @@ -1178,11 +938,10 @@ def run( the block number to start replaying from (default: None) """ - self.setup_polling_interval(polling_interval) - flashloan_tokens = self.setup_flashloan_tokens(flashloan_tokens) - CCm = self.setup_CCm(CCm) - self.logging_path = logging_path - self.replay_from_block = replay_from_block + if flashloan_tokens is None: + flashloan_tokens = self.RUN_FLASHLOAN_TOKENS + if CCm is None: + CCm = self.get_curves() try: self._run( @@ -1191,7 +950,9 @@ def run( arb_mode=arb_mode, data_validator=run_data_validator, randomizer=randomizer, + logging_path=logging_path, replay_mode=replay_mode, + replay_from_block=replay_from_block, ) except self.NoArbAvailable as e: self.ConfigObj.logger.info(e) diff --git a/fastlane_bot/events/utils.py b/fastlane_bot/events/utils.py index 6f00d5236..0993282d7 100644 --- a/fastlane_bot/events/utils.py +++ b/fastlane_bot/events/utils.py @@ -991,7 +991,6 @@ def handle_subsequent_iterations( arb_mode: str, bot: CarbonBot, flashloan_tokens: List[str], - polling_interval: int, randomizer: int, run_data_validator: bool, target_tokens: List[str] = None, @@ -1013,8 +1012,6 @@ def handle_subsequent_iterations( The bot object. flashloan_tokens : List[str] A list of flashloan tokens. - polling_interval : int - The polling interval. randomizer : int The randomizer. run_data_validator : bool @@ -1059,7 +1056,6 @@ def handle_subsequent_iterations( # Run the bot bot.run( - polling_interval=polling_interval, flashloan_tokens=flashloan_tokens, arb_mode=arb_mode, run_data_validator=run_data_validator, @@ -1802,32 +1798,6 @@ def handle_target_token_addresses(static_pool_data: pd.DataFrame, target_tokens: return target_token_addresses -def handle_replay_from_block(replay_from_block: int) -> (int, int, bool): - """ - Handle the replay from block flag. - - Parameters - ---------- - replay_from_block : int - The block number to replay from. - - Returns - ------- - polling_interval : int - The time interval at which the bot polls for new events. - - """ - if replay_from_block: - assert ( - replay_from_block > 0 - ), "The block number to replay from must be greater than 0." - reorg_delay = 0 - use_cached_events = False - polling_interval = 0 - return polling_interval, reorg_delay, use_cached_events - - -# %% def get_current_block( last_block: int, mgr: Any, diff --git a/fastlane_bot/helpers/__init__.py b/fastlane_bot/helpers/__init__.py index 8427e8c44..10d87aa2b 100644 --- a/fastlane_bot/helpers/__init__.py +++ b/fastlane_bot/helpers/__init__.py @@ -14,3 +14,4 @@ from .univ3calc import Univ3Calculator from .wrap_unwrap_processor import add_wrap_or_unwrap_trades_to_route from .carbon_trade_splitter import split_carbon_trades +from .routehandler import maximize_last_trade_per_tkn diff --git a/fastlane_bot/helpers/routehandler.py b/fastlane_bot/helpers/routehandler.py index 61b7779a4..7bfca0d1a 100644 --- a/fastlane_bot/helpers/routehandler.py +++ b/fastlane_bot/helpers/routehandler.py @@ -68,19 +68,10 @@ class RouteStruct: customData: bytes -def maximize_last_trade_per_tkn(route_struct: List[Dict[str, Any]]) -> List[Dict[str, Any]]: +def maximize_last_trade_per_tkn(route_struct: List[Dict[str, Any]]): """ Sets the source amount of the last trade to 0 per-token, ensuring that all tokens held will be used in the last trade. - - TODO: this function seems to be only used in this module and therefore should - be made a private function (_maximize_last_trade_per_tkn); I also would suggest - to move it to the TxRouteHandler class, either as static or class method. - :param route_struct: the route struct object - - Returns: - List[RouteStruct] the route struct object with the sourceAmount adjusted to 0 for each last-trade per token. - """ tkns_traded = [route_struct[0]["sourceToken"]] @@ -99,8 +90,6 @@ def maximize_last_trade_per_tkn(route_struct: List[Dict[str, Any]]) -> List[Dict route_struct[idx].sourceAmount = 0 tkns_traded.append(trade.sourceToken) - return route_struct - @dataclass class TxRouteHandler: diff --git a/fastlane_bot/tests/test_039_TestMultiMode.py b/fastlane_bot/tests/test_039_TestMultiMode.py index 226915b24..dc47761d1 100644 --- a/fastlane_bot/tests/test_039_TestMultiMode.py +++ b/fastlane_bot/tests/test_039_TestMultiMode.py @@ -179,14 +179,14 @@ def test_test_combos_and_tokens(): # + arb_finder = bot._get_arb_finder("multi") - finder2 = arb_finder( + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_TOKENS, + result=arb_finder.AO_TOKENS, ConfigObj=bot.ConfigObj, ) - all_tokens, combos = finder2.find_arbitrage() + all_tokens, combos = finder.find_arbitrage() # subjected to the length of `TAX_TOKENS` assert type(all_tokens) == set, f"[NBTest 039 TestMultiMode] all_tokens is wrong data type. Expected set, found: {type(all_tokens)}" @@ -210,7 +210,7 @@ def test_test_expected_output(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) diff --git a/fastlane_bot/tests/test_040_TestSingleMode.py b/fastlane_bot/tests/test_040_TestSingleMode.py index c037ac0ef..c7a1ee890 100644 --- a/fastlane_bot/tests/test_040_TestSingleMode.py +++ b/fastlane_bot/tests/test_040_TestSingleMode.py @@ -159,14 +159,14 @@ def test_test_tokens_and_combos(): # + arb_finder = bot._get_arb_finder("single") - finder2 = arb_finder( + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_TOKENS, + result=arb_finder.AO_TOKENS, ConfigObj=bot.ConfigObj, ) - all_tokens, combos = finder2.find_arbitrage() + all_tokens, combos = finder.find_arbitrage() assert type(all_tokens) == set, f"[TestSingleMode] all_tokens is wrong data type. Expected set, found: {type(all_tokens)}" assert type(combos) == list, f"[TestSingleMode] combos is wrong data type. Expected list, found: {type(combos)}" @@ -178,7 +178,7 @@ def test_test_tokens_and_combos(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() diff --git a/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py b/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py index 5ea6a7dc3..012c8b757 100644 --- a/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py +++ b/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py @@ -13,11 +13,11 @@ """ from fastlane_bot import Bot, Config from fastlane_bot.bot import CarbonBot +from fastlane_bot.helpers import TxRouteHandler from fastlane_bot.tools.cpc import ConstantProductCurve from fastlane_bot.tools.cpc import ConstantProductCurve as CPC from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 from fastlane_bot.events.interface import QueryInterface -from fastlane_bot.helpers import TxRouteHandler from fastlane_bot.events.managers.manager import Manager from fastlane_bot.events.interface import QueryInterface from joblib import Parallel, delayed @@ -192,7 +192,7 @@ def test_test_trade_merge(): # Convert the trade instructions ordered_trade_instructions_objects = bot._convert_trade_instructions( ordered_scaled_dcts) - tx_route_handler = bot.TxRouteHandlerClass( + tx_route_handler = TxRouteHandler( trade_instructions=ordered_trade_instructions_objects ) agg_trade_instructions = ( diff --git a/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py b/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py index 424f9a0eb..f49c68958 100644 --- a/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py +++ b/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py @@ -13,6 +13,7 @@ """ from fastlane_bot import Bot, Config from fastlane_bot.bot import CarbonBot +from fastlane_bot.helpers import TxRouteHandler from fastlane_bot.tools.cpc import ConstantProductCurve as CPC from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 from fastlane_bot.events.interface import QueryInterface @@ -217,7 +218,7 @@ def test_test_empty_carbon_orders_removed(): ordered_trade_instructions_objects = bot._convert_trade_instructions(ordered_scaled_dcts, ) # print(f"ordered_trade_instructions_objects: {ordered_trade_instructions_objects}") - tx_route_handler = bot.TxRouteHandlerClass( + tx_route_handler = TxRouteHandler( trade_instructions=ordered_trade_instructions_objects ) agg_trade_instructions = ( diff --git a/fastlane_bot/tests/test_045_Validator.py b/fastlane_bot/tests/test_045_Validator.py index 9c1b41a77..ca8732097 100644 --- a/fastlane_bot/tests/test_045_Validator.py +++ b/fastlane_bot/tests/test_045_Validator.py @@ -143,7 +143,7 @@ def test_test_validator(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() diff --git a/fastlane_bot/tests/test_047_Randomizer.py b/fastlane_bot/tests/test_047_Randomizer.py index a170d0dd7..b2f250575 100644 --- a/fastlane_bot/tests/test_047_Randomizer.py +++ b/fastlane_bot/tests/test_047_Randomizer.py @@ -145,7 +145,7 @@ def test_test_randomizer(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() @@ -161,7 +161,6 @@ def test_test_randomizer(): arb_opp_2 = bot.randomize(arb_opps=r, randomizer=1) arb_opp_3 = bot.randomize(arb_opps=r, randomizer=1) arb_opp_25 = bot.randomize(arb_opps=r, randomizer=1) - arb_opp_None = bot.randomize(arb_opps=None, randomizer=5) assert len(arb_opp_0) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_0)}" assert len(arb_opp_1) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_1)}" @@ -176,16 +175,12 @@ def test_test_randomizer(): assert isinstance(np.float64(arb_opp_3[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_3[0])}" assert isinstance(np.float64(arb_opp_25[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_25[0])}" - arb_opp_0[2] - assert type(arb_opp_0[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_0[2])}" assert type(arb_opp_1[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_1[2])}" assert type(arb_opp_2[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_2[2])}" assert type(arb_opp_3[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_3[2])}" assert type(arb_opp_25[2]) == tuple, f"[NB047 Randomizer], expected third value back from randomizer to be of type list, found type {type(arb_opp_25[2])}" - assert arb_opp_None == None, f"[NB047 Randomizer], expected randomizer to return None when it receives None, but it returned {type(arb_opp_None)}" - # ------------------------------------------------------------ # Test 047 diff --git a/fastlane_bot/tests/test_050_TestBancorV2.py b/fastlane_bot/tests/test_050_TestBancorV2.py index a58b6f53d..45a036f8f 100644 --- a/fastlane_bot/tests/test_050_TestBancorV2.py +++ b/fastlane_bot/tests/test_050_TestBancorV2.py @@ -13,6 +13,7 @@ """ from fastlane_bot import Bot, Config from fastlane_bot.bot import CarbonBot +from fastlane_bot.helpers import TxRouteHandler from fastlane_bot.tools.cpc import ConstantProductCurve as CPC from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 from fastlane_bot.events.interface import QueryInterface @@ -155,14 +156,14 @@ def test_test_combos_and_tokens(): # ------------------------------------------------------------ arb_finder = bot._get_arb_finder("multi") - finder2 = arb_finder( + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_TOKENS, + result=arb_finder.AO_TOKENS, ConfigObj=bot.ConfigObj, ) - all_tokens, combos = finder2.find_arbitrage() + all_tokens, combos = finder.find_arbitrage() assert type(all_tokens) == set, f"[NBTest_50_TestBancorV2] all_tokens is wrong data type. Expected set, found: {type(all_tokens)}" assert type(combos) == list, f"[NBTest_50_TestBancorV2] combos is wrong data type. Expected list, found: {type(combos)}" assert len(all_tokens) > 100, f"[NBTest_50_TestBancorV2] Using wrong dataset, expected at least 100 tokens, found {len(all_tokens)}" @@ -184,7 +185,7 @@ def test_test_expected_output_bancorv2(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() @@ -207,7 +208,7 @@ def test_test_expected_output_bancorv2(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() @@ -250,7 +251,7 @@ def test_test_expected_output_bancorv2(): ) # Create the tx route handler - tx_route_handler = bot.TxRouteHandlerClass( + tx_route_handler = TxRouteHandler( trade_instructions=ordered_trade_instructions_objects ) diff --git a/fastlane_bot/tests/test_053_TknMaxTrade.py b/fastlane_bot/tests/test_053_TknMaxTrade.py index 15c34fa1c..18d435081 100644 --- a/fastlane_bot/tests/test_053_TknMaxTrade.py +++ b/fastlane_bot/tests/test_053_TknMaxTrade.py @@ -15,7 +15,7 @@ from fastlane_bot import Bot from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 -from fastlane_bot.helpers.routehandler import RouteStruct, maximize_last_trade_per_tkn +from fastlane_bot.helpers import maximize_last_trade_per_tkn from fastlane_bot.tools.cpc import ConstantProductCurve as CPC print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) @@ -39,34 +39,24 @@ # Segment Test_use_0_for_sourceAmount # ------------------------------------------------------------ def test_maximize_last_trade_per_tkn(): - route_struct_0 = [ - {"sourceAmount": 10, "sourceToken": "bob_tkn", "minReturn": 10, "targetToken": "fred_tkn"}, - {"sourceAmount": 10, "sourceToken": "bob_tkn", "minReturn": 10, "targetToken": "fred_tkn"}, - {"sourceAmount": 10, "sourceToken": "fred_tkn", "minReturn": 10, "targetToken": "grog_tkn"}, - {"sourceAmount": 10, "sourceToken": "fred_tkn", "minReturn": 10, "targetToken": "grog_tkn"}, - {"sourceAmount": 10, "sourceToken": "grog_tkn", "minReturn": 10, "targetToken": "bob_tkn"}, - {"sourceAmount": 10, "sourceToken": "grog_tkn", "minReturn": 10, "targetToken": "bob_tkn"}, - - ] - - source_token = route_struct_0[0]["sourceToken"] - maximize_test_result = maximize_last_trade_per_tkn(route_struct_0) - - assert len(maximize_test_result) == len(route_struct_0) - - for trade in maximize_test_result: + route_struct = [ + {"sourceAmount": 10, "sourceToken": "bob_tkn", "minReturn": 10, "targetToken": "fred_tkn"}, + {"sourceAmount": 10, "sourceToken": "bob_tkn", "minReturn": 10, "targetToken": "fred_tkn"}, + {"sourceAmount": 10, "sourceToken": "fred_tkn", "minReturn": 10, "targetToken": "grog_tkn"}, + {"sourceAmount": 10, "sourceToken": "fred_tkn", "minReturn": 10, "targetToken": "grog_tkn"}, + {"sourceAmount": 10, "sourceToken": "grog_tkn", "minReturn": 10, "targetToken": "bob_tkn"}, + {"sourceAmount": 10, "sourceToken": "grog_tkn", "minReturn": 10, "targetToken": "bob_tkn"}, + ] + + source_token = route_struct[0]["sourceToken"] + maximize_last_trade_per_tkn(route_struct) + + for trade in route_struct: if trade["sourceToken"] == source_token: assert trade["sourceAmount"] > 0 - assert maximize_test_result[0]["sourceAmount"] == 10 - assert maximize_test_result[1]["sourceAmount"] == 10 - assert maximize_test_result[2]["sourceAmount"] == 10 - assert maximize_test_result[3]["sourceAmount"] == 0 - assert maximize_test_result[4]["sourceAmount"] == 10 - assert maximize_test_result[5]["sourceAmount"] == 0 - - # - - - - - - \ No newline at end of file + assert route_struct[0]["sourceAmount"] == 10 + assert route_struct[1]["sourceAmount"] == 10 + assert route_struct[2]["sourceAmount"] == 10 + assert route_struct[3]["sourceAmount"] == 0 + assert route_struct[4]["sourceAmount"] == 10 + assert route_struct[5]["sourceAmount"] == 0 diff --git a/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py b/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py index 448c0a805..33b0fd1c3 100644 --- a/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py +++ b/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py @@ -25,10 +25,7 @@ from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 from fastlane_bot.events.interface import QueryInterface from fastlane_bot.events.managers.manager import Manager -from fastlane_bot.helpers import ( - TxRouteHandler, - TradeInstruction, -) +from fastlane_bot.helpers import TxRouteHandler, TradeInstruction from fastlane_bot.tools.cpc import ConstantProductCurve as CPC print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) @@ -177,7 +174,7 @@ def test_test_precision_using_all_tokens_in_carbon(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() @@ -386,7 +383,7 @@ def calculate_trade_outputs(tx_route_handler: TxRouteHandler, ) # Create the tx route handler - tx_route_handler = bot.TxRouteHandlerClass( + tx_route_handler = TxRouteHandler( trade_instructions=ordered_trade_instructions_objects ) diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index e37f5d8c1..aa96f81e2 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -20,8 +20,10 @@ """ from fastlane_bot import Bot from fastlane_bot.bot import CarbonBot +from fastlane_bot.helpers import TxRouteHandler from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 +from fastlane_bot.utils import num_format from fastlane_bot.helpers import add_wrap_or_unwrap_trades_to_route, split_carbon_trades from fastlane_bot.events.managers.manager import Manager from dataclasses import asdict @@ -177,7 +179,7 @@ def init_bot(mgr: Manager) -> CarbonBot: flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() @@ -216,7 +218,7 @@ def test_wrap_unwrap_original(): ) # Create the tx route handler - tx_route_handler = bot.TxRouteHandlerClass( + tx_route_handler = TxRouteHandler( trade_instructions=ordered_trade_instructions_objects ) @@ -228,7 +230,6 @@ def test_wrap_unwrap_original(): ) # Calculate the trade instructions - # try: calculated_trade_instructions = tx_route_handler.calculate_trade_outputs(trade_instructions=agg_trade_instructions) # Aggregate multiple Bancor V3 trades into a single trade @@ -240,51 +241,38 @@ def test_wrap_unwrap_original(): # Get the flashloan token fl_token = calculated_trade_instructions[0].tknin_address + fl_token_symbol = calculated_trade_instructions[0].tknin_symbol best_profit = flashloan_tkn_profit = tx_route_handler.calculate_trade_profit( calculated_trade_instructions ) - # Use helper function to calculate profit - best_profit, flt_per_bnt, profit_usd = bot.calculate_profit( + # Calculate the best profit + best_profit_fl_token, best_profit_gastkn, best_profit_usd = bot.calculate_profit( CCm, best_profit, fl_token ) - # Log the best trade instructions - bot.handle_logging_for_trade_instructions( - 1, best_profit=best_profit # The log id - ) + # Log the best profit + cfg.logger.info(f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}") - # Use helper function to update the log dict - log_dict = bot.update_log_dict( + # Calculate the arbitrage + arb = bot.calculate_arb( arb_mode, - best_profit, - profit_usd, + best_profit_gastkn, + best_profit_usd, flashloan_tkn_profit, calculated_trade_instructions, - fl_token, + fl_token_symbol, ) - # Log the log dict - bot.handle_logging_for_trade_instructions(2, log_dict=log_dict) # The log id - - # Check if the best profit is greater than the minimum profit - # if best_profit < bot.ConfigObj.DEFAULT_MIN_PROFIT: - # bot.ConfigObj.logger.info( - # f"Opportunity with profit: {num_format(best_profit)} does not meet minimum profit: {bot.ConfigObj.DEFAULT_MIN_PROFIT}, discarding." - # ) + # Log the arbitrage + cfg.logger.info(f"calculated arb: {arb}") - # Get the flashloan amount and token address + # Get the flashloan amount flashloan_amount = int(calculated_trade_instructions[0].amtin_wei) - flashloan_token_address = bot.ConfigObj.w3.to_checksum_address( - bot.db.get_token(tkn_address=fl_token).address - ) - # Log the flashloan amount and token address - bot.handle_logging_for_trade_instructions( - 3, # The log id - flashloan_amount=flashloan_amount, - ) + # Log the flashloan amount + cfg.logger.info(f"Flashloan amount: {flashloan_amount}") split_trades = split_carbon_trades(cfg, calculated_trade_instructions) diff --git a/fastlane_bot/tests/test_063_TestBancorPOLMode.py b/fastlane_bot/tests/test_063_TestBancorPOLMode.py index da6121a54..97ade2788 100644 --- a/fastlane_bot/tests/test_063_TestBancorPOLMode.py +++ b/fastlane_bot/tests/test_063_TestBancorPOLMode.py @@ -163,14 +163,14 @@ def test_test_combos_and_tokens(): # ------------------------------------------------------------ arb_finder = bot._get_arb_finder("multi_pairwise_pol") - finder2 = arb_finder( + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_TOKENS, + result=arb_finder.AO_TOKENS, ConfigObj=bot.ConfigObj, ) - all_tokens, combos = finder2.find_arbitrage() + all_tokens, combos = finder.find_arbitrage() assert type(all_tokens) == set, f"[NBTest 063 TestMultiPairwisePOLMode] all_tokens is wrong data type. Expected set, found: {type(all_tokens)}" assert "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" in all_tokens, f"[NBTest 063 TestMultiPairwisePOLMode] Expected BNT address in all_tokens: {(all_tokens)}" assert type(combos) == list, f"[NBTest 063 TestMultiPairwisePOLMode] combos is wrong data type. Expected list, found: {type(combos)}" @@ -193,7 +193,7 @@ def test_test_expected_output(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) diff --git a/fastlane_bot/tests/test_064_TestMultiAllMode.py b/fastlane_bot/tests/test_064_TestMultiAllMode.py index fecafdcbb..8af2f4b8c 100644 --- a/fastlane_bot/tests/test_064_TestMultiAllMode.py +++ b/fastlane_bot/tests/test_064_TestMultiAllMode.py @@ -164,14 +164,14 @@ def test_test_combos_and_tokens(): # + arb_finder = bot._get_arb_finder("multi_pairwise_all") - finder2 = arb_finder( + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_TOKENS, + result=arb_finder.AO_TOKENS, ConfigObj=bot.ConfigObj, ) - all_tokens, combos = finder2.find_arbitrage() + all_tokens, combos = finder.find_arbitrage() assert type(all_tokens) == set, f"[NBTest64 TestMultiPairwiseAll Mode] all_tokens is wrong data type. Expected set, found: {type(all_tokens)}" assert type(combos) == list, f"[NBTest64 TestMultiPairwiseAll Mode] combos is wrong data type. Expected list, found: {type(combos)}" @@ -194,7 +194,7 @@ def test_test_expected_output(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() diff --git a/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py b/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py index f81e971c4..a003d4fdf 100644 --- a/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py +++ b/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py @@ -157,14 +157,14 @@ def test_test_combos(): # ------------------------------------------------------------ arb_finder = bot._get_arb_finder("multi_triangle") - finder2 = arb_finder( + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_TOKENS, + result=arb_finder.AO_TOKENS, ConfigObj=bot.ConfigObj, ) - combos = finder2.get_combos(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode="multi_triangle") + combos = finder.get_combos(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode="multi_triangle") assert len(combos) >= 1225, f"[TestMultiTriangleMode] Using wrong dataset, expected at least 1225 combos, found {len(combos)}" # + @@ -185,7 +185,7 @@ def test_test_combos(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() @@ -240,14 +240,14 @@ def test_test_combos_triangle_single(): # ------------------------------------------------------------ arb_finder = bot._get_arb_finder("triangle") - finder2 = arb_finder( + finder = arb_finder( flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_TOKENS, + result=arb_finder.AO_TOKENS, ConfigObj=bot.ConfigObj, ) - combos = finder2.get_combos(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode="multi_triangle") + combos = finder.get_combos(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode="multi_triangle") assert len(combos) >= 1225, f"[TestMultiTriangleMode] Using wrong dataset, expected at least 1225 combos, found {len(combos)}" @@ -265,7 +265,7 @@ def test_test_find_arbitrage_single(): flashloan_tokens=flashloan_tokens, CCm=CCm, mode="bothin", - result=bot.AO_CANDIDATES, + result=arb_finder.AO_CANDIDATES, ConfigObj=bot.ConfigObj, ) r = finder.find_arbitrage() diff --git a/fastlane_bot/utils.py b/fastlane_bot/utils.py index 077658087..e1692d086 100644 --- a/fastlane_bot/utils.py +++ b/fastlane_bot/utils.py @@ -9,11 +9,9 @@ All rights reserved. Licensed under MIT. """ -import datetime import glob import math import os.path -from _decimal import Decimal from dataclasses import dataclass @@ -22,39 +20,11 @@ def safe_int(value: int or float) -> int: return int(value) -def num_format(number): +def num_format(value: int or float) -> str: try: - return "{0:.4f}".format(number) - except Exception as e: - return number - - -def num_format_float(number): - try: - return float("{0:.4f}".format(number)) - except Exception as e: - return number - - -def log_format(log_data: {}, log_name: str = "new"): - now = datetime.datetime.now() - time_ts = str(int(now.timestamp())) # timestamp (epoch) - time_iso = now.isoformat().split(".")[0] - # print(time_ts) - # print(time_iso) - - log_string = f"[{time_iso}::{time_ts}] |{log_name}| == {log_data}" - return log_string - # return "\n".join("[{" + time_iso + "}::{" + time_ts + "}] |" + log_name + "| == {d}\n".format(d=(log_data))) - - -def convert_decimals(amt: Decimal, n: int) -> Decimal: - """ - Utility function to convert to Decimaling point value of a specific precision. - """ - if amt is None: - return Decimal("0") - return Decimal(str(amt / (Decimal("10") ** Decimal(str(n))))) + return "{0:.4f}".format(value) + except Exception as _: + return str(value) @dataclass diff --git a/main.py b/main.py index 02c141204..a3bc0aadf 100644 --- a/main.py +++ b/main.py @@ -50,7 +50,6 @@ set_network_to_tenderly_if_replay, verify_min_bnt_is_respected, handle_target_token_addresses, - handle_replay_from_block, get_current_block, handle_tenderly_event_exchanges, handle_static_pools_update, @@ -117,11 +116,11 @@ def main(args: argparse.Namespace) -> None: args = process_arguments(args) if args.replay_from_block or args.tenderly_fork_id: - ( - args.polling_interval, - args.reorg_delay, - args.use_cached_events, - ) = handle_replay_from_block(args.replay_from_block) + if args.replay_from_block: + assert args.replay_from_block > 0, "The block number to replay from must be greater than 0." + args.polling_interval = 0 + args.reorg_delay = 0 + args.use_cached_events = False # Set config loglevel = get_loglevel(args.loglevel) @@ -453,7 +452,6 @@ def run(mgr, args, tenderly_uri=None) -> None: arb_mode=args.arb_mode, bot=bot, flashloan_tokens=args.flashloan_tokens, - polling_interval=args.polling_interval, randomizer=args.randomizer, run_data_validator=args.run_data_validator, target_tokens=args.target_tokens,