From 7f9f5a0049c950b4d19613e54a6acd5c1bcc972f Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 12:39:57 +0300 Subject: [PATCH 01/21] Cleanup bot.py --- fastlane_bot/bot.py | 229 ++++-------------- .../tests/test_061_TestWETHConversion.py | 23 +- 2 files changed, 52 insertions(+), 200 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index da83d65fc..bd18f8ecf 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -81,7 +81,7 @@ @dataclass -class CarbonBotBase: +class CarbonBot: """ Base class for the business logic of the bot. @@ -107,6 +107,24 @@ class CarbonBotBase: min_profit: int = 60 polling_interval: int = None + RUN_POLLING_INTERVAL = 60 # default polling interval in seconds + SCALING_FACTOR = 0.999 + AO_TOKENS = "tokens" + AO_CANDIDATES = "candidates" + + 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): """ The post init method. @@ -128,26 +146,6 @@ def __post_init__(self): 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. @@ -206,38 +204,6 @@ def get_curves(self) -> CPCContainer: 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 ): @@ -324,20 +290,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. @@ -901,9 +853,9 @@ def _handle_trade_instructions( 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.log_best_profit] Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}" ) # Use helper function to update the log dict @@ -917,7 +869,9 @@ def _handle_trade_instructions( ) # Log the log dict - self.handle_logging_for_trade_instructions(2, log_dict=log_dict) # The log id + self.ConfigObj.logger.info( + f"[bot.log_calculated_arb] {log_format(log_data=log_dict, log_name='calculated_arb')}" + ) # Check if the best profit is greater than the minimum profit if best_profit_gastkn < self.ConfigObj.DEFAULT_MIN_PROFIT_GAS_TOKEN: @@ -926,13 +880,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.log_flashloan_amount] Flashloan amount: {flashloan_amount_wei}" ) # Split Carbon Orders @@ -966,127 +916,30 @@ def _handle_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, + # Log the flashloan details + self.ConfigObj.logger.debug( + f"[bot.log_flashloan_details] Flashloan of {fl_token_symbol}, amount: {flashloan_amount_wei}" + ) + self.ConfigObj.logger.debug( + f"[bot.log_flashloan_details] Flashloan token address: {fl_token}" + ) + self.ConfigObj.logger.debug( + f"[bot.log_flashloan_details] Route Struct: \n {route_struct_maximized}" + ) + self.ConfigObj.logger.debug( + f"[bot.log_flashloan_details] Trade Instructions: \n {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, + src_address=fl_token, 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. - - Parameters - ---------- - flashloan_amount : Optional[float], optional - The flashloan amount, by default None - """ - 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}" - ) - self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Flashloan token address: {flashloan_token_address}" - ) - self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Route Struct: \n {route_struct}" - ) - self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] 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. diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index e1b969e29..d15a96f97 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -22,6 +22,7 @@ from fastlane_bot.bot import CarbonBot from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 +from fastlane_bot.utils import num_format, log_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 @@ -250,9 +251,9 @@ def test_wrap_unwrap_original(): 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.debug( + f"[bot.log_best_profit] Updated best_profit after calculating exact trade numbers: {num_format(best_profit)}" ) # Use helper function to update the log dict @@ -266,7 +267,9 @@ def test_wrap_unwrap_original(): ) # Log the log dict - bot.handle_logging_for_trade_instructions(2, log_dict=log_dict) # The log id + cfg.logger.info( + f"[bot.log_calculated_arb] {log_format(log_data=log_dict, log_name='calculated_arb')}" + ) # Check if the best profit is greater than the minimum profit # if best_profit < bot.ConfigObj.DEFAULT_MIN_PROFIT: @@ -274,16 +277,12 @@ def test_wrap_unwrap_original(): # f"Opportunity with profit: {num_format(best_profit)} does not meet minimum profit: {bot.ConfigObj.DEFAULT_MIN_PROFIT}, discarding." # ) - # 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.debug( + f"[bot.log_flashloan_amount] Flashloan amount: {flashloan_amount}" ) split_trades = split_carbon_trades(cfg, calculated_trade_instructions) From 18430f4c0fea67413466c0bd9f77dc3fd260d1fb Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 14:08:26 +0300 Subject: [PATCH 02/21] Remove unused variables --- fastlane_bot/bot.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index bd18f8ecf..3d465f1a2 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -103,8 +103,6 @@ class CarbonBot: TxRouteHandlerClass: any = None TxHelpersClass: any = None ConfigObj: Config = None - usd_gas_limit: int = 150 - min_profit: int = 60 polling_interval: int = None RUN_POLLING_INTERVAL = 60 # default polling interval in seconds From ae52e0ec44e016fbb735f18647ace5e25d7894eb Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 14:39:23 +0300 Subject: [PATCH 03/21] Remove redundancy of constant values declared in the `bot` module --- fastlane_bot/bot.py | 9 ++++----- fastlane_bot/tests/test_039_TestMultiMode.py | 8 ++++---- fastlane_bot/tests/test_040_TestSingleMode.py | 8 ++++---- fastlane_bot/tests/test_045_Validator.py | 2 +- fastlane_bot/tests/test_047_Randomizer.py | 2 +- fastlane_bot/tests/test_050_TestBancorV2.py | 10 +++++----- .../test_060_TestRoutehandlerCarbonPrecision.py | 2 +- .../tests/test_061_TestWETHConversion.py | 2 +- fastlane_bot/tests/test_063_TestBancorPOLMode.py | 8 ++++---- fastlane_bot/tests/test_064_TestMultiAllMode.py | 8 ++++---- .../tests/test_901_TestMultiTriangleModeSlow.py | 16 ++++++++-------- 11 files changed, 37 insertions(+), 38 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 3d465f1a2..cdef2b3a1 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -107,8 +107,6 @@ class CarbonBot: RUN_POLLING_INTERVAL = 60 # default polling interval in seconds SCALING_FACTOR = 0.999 - AO_TOKENS = "tokens" - AO_CANDIDATES = "candidates" ARB_FINDER = { "single": FindArbitrageSinglePairwise, @@ -316,15 +314,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, diff --git a/fastlane_bot/tests/test_039_TestMultiMode.py b/fastlane_bot/tests/test_039_TestMultiMode.py index 0b8a7ff8e..3fecfe6c8 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 db6b531ee..2ccd2682d 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_045_Validator.py b/fastlane_bot/tests/test_045_Validator.py index e47bfe4a6..0e6f283ce 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 5d91b9c1b..3cd9fb758 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() diff --git a/fastlane_bot/tests/test_050_TestBancorV2.py b/fastlane_bot/tests/test_050_TestBancorV2.py index 10f112277..66b2335b4 100644 --- a/fastlane_bot/tests/test_050_TestBancorV2.py +++ b/fastlane_bot/tests/test_050_TestBancorV2.py @@ -155,14 +155,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 +184,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 +207,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() diff --git a/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py b/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py index 10e9a4525..33036eb6a 100644 --- a/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py +++ b/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py @@ -177,7 +177,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() diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index d15a96f97..929c83b46 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -178,7 +178,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() diff --git a/fastlane_bot/tests/test_063_TestBancorPOLMode.py b/fastlane_bot/tests/test_063_TestBancorPOLMode.py index 1088e0772..55e76adb5 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 490ea6202..0bbe050c6 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 5c0c6355d..78651e843 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() From 6d010532f060d7a4d944a1642a1ffea596de2c5f Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 14:49:34 +0300 Subject: [PATCH 04/21] Replace `TxRouteHandlerClass` with `TxRouteHandler` --- fastlane_bot/bot.py | 9 +-------- fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py | 4 ++-- fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py | 3 ++- fastlane_bot/tests/test_050_TestBancorV2.py | 3 ++- .../tests/test_060_TestRoutehandlerCarbonPrecision.py | 7 ++----- fastlane_bot/tests/test_061_TestWETHConversion.py | 3 ++- 6 files changed, 11 insertions(+), 18 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index cdef2b3a1..6e2e6fa3d 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -67,7 +67,6 @@ ) 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 @@ -89,8 +88,6 @@ class CarbonBot: ---------- db: DatabaseManager the database manager. - TxRouteHandlerClass - ditto (default: TxRouteHandler). TxHelpersClass: ditto (default: TxHelpers). @@ -100,7 +97,6 @@ class CarbonBot: __DATE__ = __DATE__ db: QueryInterface = field(init=False) - TxRouteHandlerClass: any = None TxHelpersClass: any = None ConfigObj: Config = None polling_interval: int = None @@ -133,9 +129,6 @@ def __post_init__(self): 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) @@ -808,7 +801,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 ) diff --git a/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py b/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py index 6c7aea057..598c47844 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 0037915a8..70127ce92 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_050_TestBancorV2.py b/fastlane_bot/tests/test_050_TestBancorV2.py index 66b2335b4..1a3265395 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 @@ -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_060_TestRoutehandlerCarbonPrecision.py b/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py index 33036eb6a..6b85cdd8b 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)) @@ -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 929c83b46..8ee719782 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -20,6 +20,7 @@ """ 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, log_format @@ -217,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 ) From e716f544d2d7903d688db7ab24aa47ba1ad3f7e7 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 15:19:14 +0300 Subject: [PATCH 05/21] Some more cleanups --- .../tests/test_061_TestWETHConversion.py | 12 +++-------- fastlane_bot/utils.py | 21 +++---------------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index 8ee719782..ca760f1cc 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -253,9 +253,7 @@ def test_wrap_unwrap_original(): ) # Log the best profit - cfg.logger.debug( - f"[bot.log_best_profit] Updated best_profit after calculating exact trade numbers: {num_format(best_profit)}" - ) + cfg.logger.debug(f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit)}") # Use helper function to update the log dict log_dict = bot.update_log_dict( @@ -268,9 +266,7 @@ def test_wrap_unwrap_original(): ) # Log the log dict - cfg.logger.info( - f"[bot.log_calculated_arb] {log_format(log_data=log_dict, log_name='calculated_arb')}" - ) + cfg.logger.info(log_format(log_data=log_dict, log_name='calculated_arb')) # Check if the best profit is greater than the minimum profit # if best_profit < bot.ConfigObj.DEFAULT_MIN_PROFIT: @@ -282,9 +278,7 @@ def test_wrap_unwrap_original(): flashloan_amount = int(calculated_trade_instructions[0].amtin_wei) # Log the flashloan amount - cfg.logger.debug( - f"[bot.log_flashloan_amount] Flashloan amount: {flashloan_amount}" - ) + cfg.logger.debug(f"Flashloan amount: {flashloan_amount}") split_trades = split_carbon_trades(cfg, calculated_trade_instructions) diff --git a/fastlane_bot/utils.py b/fastlane_bot/utils.py index 077658087..c711be4be 100644 --- a/fastlane_bot/utils.py +++ b/fastlane_bot/utils.py @@ -13,7 +13,6 @@ import glob import math import os.path -from _decimal import Decimal from dataclasses import dataclass @@ -36,25 +35,11 @@ def num_format_float(number): return number -def log_format(log_data: {}, log_name: str = "new"): +def log_format(log_data: dict, log_name: str) -> str: now = datetime.datetime.now() - time_ts = str(int(now.timestamp())) # timestamp (epoch) + time_ts = str(int(now.timestamp())) 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 f"[{time_iso}::{time_ts}] |{log_name}| == {log_data}" @dataclass From aea4f8ca5b8807c4d4027e2819b22ad0ece951bc Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 15:25:42 +0300 Subject: [PATCH 06/21] Some more cleanups --- fastlane_bot/bot.py | 14 +++++++------- fastlane_bot/utils.py | 15 ++++----------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 6e2e6fa3d..fed75a0e3 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -76,7 +76,7 @@ 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, log_format @dataclass @@ -722,14 +722,14 @@ def update_log_dict( flashloans = [ { "token": fl_token, - "amount": num_format_float(calculated_trade_instructions[0].amtin), - "profit": num_format_float(flashloan_tkn_profit), + "amount": num_format(calculated_trade_instructions[0].amtin), + "profit": num_format(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), + "profit_gas_token": num_format(best_profit_gastkn), + "profit_usd": num_format(best_profit_usd), "flashloan": flashloans, "trades": [], } @@ -742,9 +742,9 @@ def update_log_dict( "trade_index": idx, "exchange": trade.exchange_name, "tkn_in": tknin, - "amount_in": num_format_float(trade.amtin), + "amount_in": num_format(trade.amtin), "tkn_out": tknout, - "amt_out": num_format_float(trade.amtout), + "amt_out": num_format(trade.amtout), "cid0": trade.cid[-10:], } ) diff --git a/fastlane_bot/utils.py b/fastlane_bot/utils.py index c711be4be..0e955d426 100644 --- a/fastlane_bot/utils.py +++ b/fastlane_bot/utils.py @@ -21,18 +21,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 + return "{0:.4f}".format(value) + except Exception as _: + return str(value) def log_format(log_data: dict, log_name: str) -> str: From ec086ab50a955e495619551089cef102be4a3c4c Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 16:04:51 +0300 Subject: [PATCH 07/21] Remove redundant version checking --- fastlane_bot/bot.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index fed75a0e3..3326d7327 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -174,10 +174,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" From 381920e08c336499978c7d8be872e8fb7928333c Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 16:24:46 +0300 Subject: [PATCH 08/21] Fix logging in `bot.py` --- fastlane_bot/bot.py | 52 +++++++++++---------------------------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index a5a080d29..2704e90a4 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -500,16 +500,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 ( @@ -545,7 +537,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 @@ -571,26 +563,6 @@ def randomize(arb_opps, randomizer: int = 1): 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}") - @staticmethod def _carbon_in_trade_route(trade_instructions: List[TradeInstruction]) -> bool: """ @@ -650,7 +622,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 @@ -684,7 +656,7 @@ 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 @@ -848,7 +820,7 @@ def _handle_trade_instructions( # Log the best profit self.ConfigObj.logger.debug( - f"[bot.log_best_profit] Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}" + 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 @@ -863,7 +835,7 @@ def _handle_trade_instructions( # Log the log dict self.ConfigObj.logger.info( - f"[bot.log_calculated_arb] {log_format(log_data=log_dict, log_name='calculated_arb')}" + f"[bot._handle_trade_instructions] {log_format(log_data=log_dict, log_name='calculated_arb')}" ) # Check if the best profit is greater than the minimum profit @@ -875,7 +847,7 @@ def _handle_trade_instructions( # Log the flashloan amount self.ConfigObj.logger.debug( - f"[bot.log_flashloan_amount] Flashloan amount: {flashloan_amount_wei}" + f"[bot._handle_trade_instructions] Flashloan amount: {flashloan_amount_wei}" ) # Split Carbon Orders @@ -911,16 +883,16 @@ def _handle_trade_instructions( # Log the flashloan details self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Flashloan of {fl_token_symbol}, amount: {flashloan_amount_wei}" + 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: {fl_token}" + f"[bot._handle_trade_instructions] Flashloan token address: {fl_token}" ) self.ConfigObj.logger.debug( - f"[bot.log_flashloan_details] Route Struct: \n {route_struct_maximized}" + f"[bot._handle_trade_instructions] Route Struct: \n {route_struct_maximized}" ) 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}" ) # Validate and submit the transaction From 655ff2115860fc6d1756e091935e53af756a3b67 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 16:56:32 +0300 Subject: [PATCH 09/21] Minor cleanup --- fastlane_bot/bot.py | 20 +++++++++----------- fastlane_bot/helpers/__init__.py | 1 + fastlane_bot/tests/test_053_TknMaxTrade.py | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 2704e90a4..7d2061b30 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,9 +62,9 @@ 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 .config.constants import FLASHLOAN_FEE_MAP from .events.interface import QueryInterface @@ -86,18 +85,17 @@ class CarbonBot: Attributes ---------- - db: DatabaseManager + db: QueryInterface the database manager. - TxHelpersClass: - ditto (default: TxHelpers). - + tx_helpers: TxHelpers + the tx-helpers utility. """ __VERSION__ = __VERSION__ __DATE__ = __DATE__ db: QueryInterface = field(init=False) - TxHelpersClass: any = None + tx_helpers: TxHelpers = None ConfigObj: Config = None polling_interval: int = None @@ -129,8 +127,8 @@ def __post_init__(self): self.polling_interval is None ), "polling_interval is now a parameter to run" - 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()] @@ -896,7 +894,7 @@ def _handle_trade_instructions( ) # Validate and submit the transaction - return self.TxHelpersClass.validate_and_submit_transaction( + return self.tx_helpers.validate_and_submit_transaction( route_struct=route_struct_maximized, src_amt=flashloan_amount_wei, src_address=fl_token, 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/tests/test_053_TknMaxTrade.py b/fastlane_bot/tests/test_053_TknMaxTrade.py index 15c34fa1c..9bb5a9705 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)) From fdc7fed59cca065504d382a4dcb4b4b77e30765d Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 17:06:08 +0300 Subject: [PATCH 10/21] Get rid of function maximize_last_trade_per_tkn's return-value, since this function changes the input in place --- fastlane_bot/bot.py | 6 +-- fastlane_bot/helpers/routehandler.py | 13 +----- fastlane_bot/tests/test_053_TknMaxTrade.py | 48 +++++++++------------- 3 files changed, 23 insertions(+), 44 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 7d2061b30..ff86c9cbc 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -877,7 +877,7 @@ def _handle_trade_instructions( trade_instructions=split_calculated_trade_instructions, ) - route_struct_maximized = maximize_last_trade_per_tkn(route_struct=route_struct_processed) + maximize_last_trade_per_tkn(route_struct=route_struct_processed) # Log the flashloan details self.ConfigObj.logger.debug( @@ -887,7 +887,7 @@ def _handle_trade_instructions( f"[bot._handle_trade_instructions] Flashloan token address: {fl_token}" ) self.ConfigObj.logger.debug( - f"[bot._handle_trade_instructions] Route Struct: \n {route_struct_maximized}" + f"[bot._handle_trade_instructions] Route Struct: \n {route_struct_processed}" ) self.ConfigObj.logger.debug( f"[bot._handle_trade_instructions] Trade Instructions: \n {best_trade_instructions_dic}" @@ -895,7 +895,7 @@ def _handle_trade_instructions( # Validate and submit the transaction return self.tx_helpers.validate_and_submit_transaction( - route_struct=route_struct_maximized, + route_struct=route_struct_processed, src_amt=flashloan_amount_wei, src_address=fl_token, expected_profit_gastkn=best_profit_gastkn, 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_053_TknMaxTrade.py b/fastlane_bot/tests/test_053_TknMaxTrade.py index 9bb5a9705..18d435081 100644 --- a/fastlane_bot/tests/test_053_TknMaxTrade.py +++ b/fastlane_bot/tests/test_053_TknMaxTrade.py @@ -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 From 437031611efc8e32721596902f46ff09914c4378 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 18:13:06 +0300 Subject: [PATCH 11/21] Remove unused `RUN_POLLING_INTERVAL` --- fastlane_bot/bot.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index ff86c9cbc..852b9bb37 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -99,7 +99,6 @@ class CarbonBot: ConfigObj: Config = None polling_interval: int = None - RUN_POLLING_INTERVAL = 60 # default polling interval in seconds SCALING_FACTOR = 0.999 ARB_FINDER = { @@ -903,17 +902,6 @@ def _handle_trade_instructions( flashloan_struct=flashloan_struct, ) - 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. @@ -986,7 +974,7 @@ def run( 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) + the polling interval in seconds arb_mode: str the arbitrage mode (default: None; can be set depending on arbmode) run_data_validator: bool @@ -1001,7 +989,8 @@ def run( the block number to start replaying from (default: None) """ - self.setup_polling_interval(polling_interval) + if self.polling_interval is None: + self.polling_interval = polling_interval flashloan_tokens = self.setup_flashloan_tokens(flashloan_tokens) CCm = self.setup_CCm(CCm) self.logging_path = logging_path From bda1cc27f9f6ae3f5aa1105be47448b9aedf4d21 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 18:29:57 +0300 Subject: [PATCH 12/21] More cleanup --- fastlane_bot/bot.py | 55 +++--------------------------------- fastlane_bot/events/utils.py | 30 -------------------- main.py | 12 ++++---- 3 files changed, 9 insertions(+), 88 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 852b9bb37..919ea2edb 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -97,7 +97,6 @@ class CarbonBot: db: QueryInterface = field(init=False) tx_helpers: TxHelpers = None ConfigObj: Config = None - polling_interval: int = None SCALING_FACTOR = 0.999 @@ -122,10 +121,6 @@ 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.tx_helpers is None: self.tx_helpers = TxHelpers(cfg=self.ConfigObj) @@ -902,45 +897,6 @@ def _handle_trade_instructions( flashloan_struct=flashloan_struct, ) - 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 - ) - - 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() - if self.ConfigObj.ARB_CONTRACT_VERSION < 10: - filter_out_weth = [ - x - for x in CCm - if (x.params.exchange in self.ConfigObj.CARBON_V1_FORKS) - & ( - (x.params.tkny_addr == self.ConfigObj.WETH_ADDRESS) - or (x.params.tknx_addr == self.ConfigObj.WETH_ADDRESS) - ) - ] - CCm = CPCContainer([x for x in CCm if x not in filter_out_weth]) - return CCm - def get_tokens_in_exchange( self, exchange_name: str, @@ -956,7 +912,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, @@ -973,8 +928,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 arb_mode: str the arbitrage mode (default: None; can be set depending on arbmode) run_data_validator: bool @@ -989,10 +942,10 @@ def run( the block number to start replaying from (default: None) """ - if self.polling_interval is None: - self.polling_interval = polling_interval - flashloan_tokens = self.setup_flashloan_tokens(flashloan_tokens) - CCm = self.setup_CCm(CCm) + if flashloan_tokens is None: + flashloan_tokens = self.RUN_FLASHLOAN_TOKENS + if CCm is None: + CCm = self.get_curves() self.logging_path = logging_path self.replay_from_block = replay_from_block 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/main.py b/main.py index c05d3443f..169885117 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, @@ -118,11 +117,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) @@ -455,7 +454,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, From b1fa4f91d40f86da4c3b27c9cc910dd29bc1e95f Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 18:40:37 +0300 Subject: [PATCH 13/21] Remove the `logging_path` attribute from the `bot` module --- fastlane_bot/bot.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 919ea2edb..87676fbb9 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -318,6 +318,7 @@ def _run( arb_mode: str, randomizer: int, data_validator=False, + logging_path: str = None, replay_mode: bool = False, ): """ @@ -335,6 +336,10 @@ 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) """ arbitrage = self._find_arbitrage(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode=arb_mode, randomizer=randomizer) @@ -375,9 +380,9 @@ def _run( 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): @@ -946,7 +951,6 @@ def run( flashloan_tokens = self.RUN_FLASHLOAN_TOKENS if CCm is None: CCm = self.get_curves() - self.logging_path = logging_path self.replay_from_block = replay_from_block try: @@ -956,6 +960,7 @@ def run( arb_mode=arb_mode, data_validator=run_data_validator, randomizer=randomizer, + logging_path=logging_path, replay_mode=replay_mode, ) except self.NoArbAvailable as e: From 90699385a28f8ba82cdf451d62a18a8c09b13384 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 18:48:36 +0300 Subject: [PATCH 14/21] Remove the `replay_from_block` attribute from the `bot` module --- fastlane_bot/bot.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 87676fbb9..7aa78072e 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -320,6 +320,7 @@ def _run( data_validator=False, logging_path: str = None, replay_mode: bool = False, + replay_from_block: int = None, ): """ Runs the bot. @@ -340,6 +341,8 @@ def _run( 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) @@ -373,7 +376,7 @@ 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" @@ -724,7 +727,8 @@ 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 @@ -740,6 +744,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 ------- @@ -859,7 +865,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 = [ @@ -951,7 +957,6 @@ def run( flashloan_tokens = self.RUN_FLASHLOAN_TOKENS if CCm is None: CCm = self.get_curves() - self.replay_from_block = replay_from_block try: self._run( @@ -962,6 +967,7 @@ def run( 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) From 39019d54e2e8cd57dd6ab298a613ac334f75eca2 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 22:21:52 +0300 Subject: [PATCH 15/21] Cleanup --- fastlane_bot/bot.py | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 7aa78072e..b625b9739 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -691,37 +691,31 @@ def update_log_dict( dict The updated log dictionary. """ - flashloans = [ - { - "token": fl_token, - "amount": num_format(calculated_trade_instructions[0].amtin), - "profit": num_format(flashloan_tkn_profit), - } - ] - log_dict = { + + return { "type": arb_mode, "profit_gas_token": num_format(best_profit_gastkn), "profit_usd": num_format(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( + "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, + "tkn_in": {trade.tknin_symbol: trade.tknin} if trade.tknin_symbol != trade.tknin else trade.tknin, "amount_in": num_format(trade.amtin), - "tkn_out": tknout, + "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:], + "cid0": trade.cid[-10:] } - ) - - return log_dict + for idx, trade in enumerate(calculated_trade_instructions) + ] + } def _handle_trade_instructions( self, From 82f878a12727a247a973da3771e2eadc91befcec Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sat, 20 Apr 2024 23:30:07 +0300 Subject: [PATCH 16/21] Cleanup --- fastlane_bot/bot.py | 27 +++++++++++-------- .../tests/test_061_TestWETHConversion.py | 10 +++---- fastlane_bot/utils.py | 8 ------ 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index b625b9739..c8c2058d9 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -75,7 +75,7 @@ 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 +from .utils import num_format @dataclass @@ -660,16 +660,16 @@ def calculate_profit( return best_profit_fl_token, best_profit_gastkn, best_profit_usd @staticmethod - def update_log_dict( + def format_calculated_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]: + ) -> str: """ - Update the log dictionary. + Formats the calculated arbitrage. Parameters ---------- @@ -688,11 +688,11 @@ def update_log_dict( Returns ------- - dict - The updated log dictionary. + str + The calculated arbitrage. """ - return { + arb = { "type": arb_mode, "profit_gas_token": num_format(best_profit_gastkn), "profit_usd": num_format(best_profit_usd), @@ -717,6 +717,11 @@ def update_log_dict( ] } + now = datetime.datetime.now() + time_ts = str(int(now.timestamp())) + time_iso = now.isoformat().split(".")[0] + return f"[{time_iso}::{time_ts}] calculated arb = {json.dumps(arb, indent=4)}" + def _handle_trade_instructions( self, CCm: CPCContainer, @@ -820,8 +825,8 @@ def _handle_trade_instructions( 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( + # Format the calculate arbitrage + calculated_arb = self.format_calculated_arb( arb_mode, best_profit_gastkn, best_profit_usd, @@ -830,9 +835,9 @@ def _handle_trade_instructions( fl_token_symbol, ) - # Log the log dict + # Log the calculate arbitrage self.ConfigObj.logger.info( - f"[bot._handle_trade_instructions] {log_format(log_data=log_dict, log_name='calculated_arb')}" + f"[bot._handle_trade_instructions] {calculated_arb}" ) # Check if the best profit is greater than the minimum profit diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index cfc51523b..4577258d2 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -23,7 +23,7 @@ from fastlane_bot.helpers import TxRouteHandler from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 -from fastlane_bot.utils import num_format, log_format +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 @@ -255,8 +255,8 @@ def test_wrap_unwrap_original(): # Log the best profit cfg.logger.debug(f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit)}") - # Use helper function to update the log dict - log_dict = bot.update_log_dict( + # Format the calculate arbitrage + calculated_arb = bot.format_calculated_arb( arb_mode, best_profit, profit_usd, @@ -265,8 +265,8 @@ def test_wrap_unwrap_original(): fl_token, ) - # Log the log dict - cfg.logger.info(log_format(log_data=log_dict, log_name='calculated_arb')) + # Log the calculate arbitrage + cfg.logger.info(calculated_arb) # Check if the best profit is greater than the minimum profit # if best_profit < bot.ConfigObj.DEFAULT_MIN_PROFIT: diff --git a/fastlane_bot/utils.py b/fastlane_bot/utils.py index 0e955d426..e1692d086 100644 --- a/fastlane_bot/utils.py +++ b/fastlane_bot/utils.py @@ -9,7 +9,6 @@ All rights reserved. Licensed under MIT. """ -import datetime import glob import math import os.path @@ -28,13 +27,6 @@ def num_format(value: int or float) -> str: return str(value) -def log_format(log_data: dict, log_name: str) -> str: - now = datetime.datetime.now() - time_ts = str(int(now.timestamp())) - time_iso = now.isoformat().split(".")[0] - return f"[{time_iso}::{time_ts}] |{log_name}| == {log_data}" - - @dataclass class EncodedOrder: """ From f00c11ab073853dacc37afe56eb482e30dfab4b9 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 21 Apr 2024 00:01:46 +0300 Subject: [PATCH 17/21] Cleanup --- fastlane_bot/bot.py | 29 ++++++++----------- .../tests/test_061_TestWETHConversion.py | 26 ++++++++++------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index c8c2058d9..f918b234a 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -178,7 +178,7 @@ 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" ) @@ -660,16 +660,16 @@ def calculate_profit( return best_profit_fl_token, best_profit_gastkn, best_profit_usd @staticmethod - def format_calculated_arb( + 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, - ) -> str: + ) -> dict: """ - Formats the calculated arbitrage. + Calculate the arbitrage. Parameters ---------- @@ -688,11 +688,11 @@ def format_calculated_arb( Returns ------- - str - The calculated arbitrage. + dict + The arbitrage. """ - arb = { + return { "type": arb_mode, "profit_gas_token": num_format(best_profit_gastkn), "profit_usd": num_format(best_profit_usd), @@ -717,11 +717,6 @@ def format_calculated_arb( ] } - now = datetime.datetime.now() - time_ts = str(int(now.timestamp())) - time_iso = now.isoformat().split(".")[0] - return f"[{time_iso}::{time_ts}] calculated arb = {json.dumps(arb, indent=4)}" - def _handle_trade_instructions( self, CCm: CPCContainer, @@ -815,7 +810,7 @@ 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 ) @@ -825,8 +820,8 @@ def _handle_trade_instructions( f"[bot._handle_trade_instructions] Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}" ) - # Format the calculate arbitrage - calculated_arb = self.format_calculated_arb( + # Calculate the arbitrage + arb = self.calculate_arb( arb_mode, best_profit_gastkn, best_profit_usd, @@ -835,9 +830,9 @@ def _handle_trade_instructions( fl_token_symbol, ) - # Log the calculate arbitrage + # Log the arbitrage self.ConfigObj.logger.info( - f"[bot._handle_trade_instructions] {calculated_arb}" + f"[bot._handle_trade_instructions] calculated arb: {json.dumps(arb, indent=4)}" ) # Check if the best profit is greater than the minimum profit diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index 4577258d2..be3722476 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -230,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 @@ -242,31 +241,36 @@ 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 profit - cfg.logger.debug(f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit)}") + cfg.logger.debug( + f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}" + ) - # Format the calculate arbitrage - calculated_arb = bot.format_calculated_arb( + # 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 calculate arbitrage - cfg.logger.info(calculated_arb) + # Log the arbitrage + cfg.logger.info( + f"calculated arb: {json.dumps(arb, indent=4)}" + ) # Check if the best profit is greater than the minimum profit # if best_profit < bot.ConfigObj.DEFAULT_MIN_PROFIT: From e1d9880b89cd94353bcbb91a84b8efbcfc5864be Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 21 Apr 2024 00:04:24 +0300 Subject: [PATCH 18/21] Cleanup --- .../tests/test_061_TestWETHConversion.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index be3722476..ecbef3904 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -253,9 +253,7 @@ def test_wrap_unwrap_original(): ) # Log the best profit - cfg.logger.debug( - f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}" - ) + cfg.logger.info(f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}") # Calculate the arbitrage arb = bot.calculate_arb( @@ -268,21 +266,13 @@ def test_wrap_unwrap_original(): ) # Log the arbitrage - cfg.logger.info( - f"calculated arb: {json.dumps(arb, indent=4)}" - ) - - # 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." - # ) + cfg.logger.info(f"calculated arb: {json.dumps(arb, indent=4)}") # Get the flashloan amount flashloan_amount = int(calculated_trade_instructions[0].amtin_wei) # Log the flashloan amount - cfg.logger.debug(f"Flashloan amount: {flashloan_amount}") + cfg.logger.info(f"Flashloan amount: {flashloan_amount}") split_trades = split_carbon_trades(cfg, calculated_trade_instructions) From 710dcf2480a23895c9b8d87c3d019e1e37df0129 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 21 Apr 2024 00:42:31 +0300 Subject: [PATCH 19/21] Reduce printouts --- fastlane_bot/bot.py | 2 +- fastlane_bot/tests/test_061_TestWETHConversion.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index f918b234a..b4f91135b 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -832,7 +832,7 @@ def _handle_trade_instructions( # Log the arbitrage self.ConfigObj.logger.info( - f"[bot._handle_trade_instructions] calculated arb: {json.dumps(arb, indent=4)}" + f"[bot._handle_trade_instructions] calculated arb: {arb}" ) # Check if the best profit is greater than the minimum profit diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index ecbef3904..aa96f81e2 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -266,7 +266,7 @@ def test_wrap_unwrap_original(): ) # Log the arbitrage - cfg.logger.info(f"calculated arb: {json.dumps(arb, indent=4)}") + cfg.logger.info(f"calculated arb: {arb}") # Get the flashloan amount flashloan_amount = int(calculated_trade_instructions[0].amtin_wei) From 23e9f3691d4b6e47933fdbf6afcbb6146b4f0b2f Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 21 Apr 2024 06:14:49 +0300 Subject: [PATCH 20/21] Simplify function `npt.randomize` --- fastlane_bot/bot.py | 13 ++++--------- fastlane_bot/tests/test_047_Randomizer.py | 5 ----- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index b4f91135b..f67a7bcae 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -553,15 +553,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 + 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: diff --git a/fastlane_bot/tests/test_047_Randomizer.py b/fastlane_bot/tests/test_047_Randomizer.py index eda94a93a..b2f250575 100644 --- a/fastlane_bot/tests/test_047_Randomizer.py +++ b/fastlane_bot/tests/test_047_Randomizer.py @@ -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 From bf516783bbfe6bd8c79b79a1cd270040f877df9c Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 21 Apr 2024 06:26:14 +0300 Subject: [PATCH 21/21] Fix logging --- fastlane_bot/bot.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index f67a7bcae..08e8c02cd 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -150,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"