From cf07a98bfdf040b382245256bb0d28a08a41eadd Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Mon, 13 May 2024 10:32:44 +0300 Subject: [PATCH 01/18] Fix several issue in the triangle-mode implementation --- fastlane_bot/modes/base_triangle.py | 8 +------- fastlane_bot/modes/triangle_multi_complete.py | 13 +++---------- fastlane_bot/tests/test_064_TestMultiAllMode.py | 2 +- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/fastlane_bot/modes/base_triangle.py b/fastlane_bot/modes/base_triangle.py index 5da54b134..8ce78a7d1 100644 --- a/fastlane_bot/modes/base_triangle.py +++ b/fastlane_bot/modes/base_triangle.py @@ -17,12 +17,10 @@ from fastlane_bot.modes.base import ArbitrageFinderBase from fastlane_bot.tools.cpc import T -@staticmethod def sort_pairs(pairs): # Clean up the pairs alphabetically return ["/".join(sorted(pair.split('/'))) for pair in pairs] -@staticmethod def flatten_nested_items_in_list(nested_list): # unpack nested items flattened_list = [] @@ -36,7 +34,6 @@ def flatten_nested_items_in_list(nested_list): flattened_list.append(flat_list) return flattened_list -@staticmethod def get_triangle_groups(flt, x_y_pairs): # Get groups of triangles that conform to (flt/x , x/y, y/flt) where x!=y triangle_groups = [] @@ -45,7 +42,6 @@ def get_triangle_groups(flt, x_y_pairs): triangle_groups += [("/".join(sorted([flt,x])), pair, "/".join(sorted([flt,y])))] return triangle_groups -@staticmethod def get_triangle_groups_stats(triangle_groups, all_relevant_pairs_info): # Get the stats on the triangle group cohort for decision making valid_carbon_triangles = [] @@ -250,7 +246,7 @@ def get_analysis_set_per_flt(self, flt, valid_triangles, all_relevant_pairs_info return flt_triangle_analysis_set def get_comprehensive_triangles( - self, flashloan_tokens: List[str], CCm: Any, arb_mode: str + self, flashloan_tokens: List[str], CCm: Any ) -> Tuple[List[str], List[Any]]: """ Get comprehensive combos for triangular arbitrage @@ -261,8 +257,6 @@ def get_comprehensive_triangles( List of flashloan tokens CCm : object CCm object - arb_mode : str - Arbitrage mode Returns ------- diff --git a/fastlane_bot/modes/triangle_multi_complete.py b/fastlane_bot/modes/triangle_multi_complete.py index eb16e655c..e2ee8c947 100644 --- a/fastlane_bot/modes/triangle_multi_complete.py +++ b/fastlane_bot/modes/triangle_multi_complete.py @@ -30,30 +30,23 @@ def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_p if candidates is None: candidates = [] - combos = self.get_comprehensive_triangles( - self.flashloan_tokens, self.CCm, arb_mode=self.arb_mode - ) + combos = self.get_comprehensive_triangles(self.flashloan_tokens, self.CCm) for src_token, miniverse in combos: try: - r = None CC_cc = CPCContainer(miniverse) O = MargPOptimizer(CC_cc) pstart = self.build_pstart(CC_cc, CC_cc.tokens(), src_token) r = O.optimize(src_token, params=dict(pstart=pstart)) #debug=True, debug2=True, verbose=True trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - if len(trade_instructions_dic) < 3: + if trade_instructions_dic is None or len(trade_instructions_dic) < 3: # Failed to converge continue trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) trade_instructions = r.trade_instructions() except Exception as e: - self.ConfigObj.logger.debug(f"[triangle multi] {str(e)}") - continue - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 2: + self.ConfigObj.logger.info(f"[triangle multi] {e}") continue profit_src = -r.result diff --git a/fastlane_bot/tests/test_064_TestMultiAllMode.py b/fastlane_bot/tests/test_064_TestMultiAllMode.py index 033818012..6d7ff45be 100644 --- a/fastlane_bot/tests/test_064_TestMultiAllMode.py +++ b/fastlane_bot/tests/test_064_TestMultiAllMode.py @@ -224,7 +224,7 @@ def test_test_expected_output(): assert len(r) >= 25, f"[NBTest64 TestMultiPairwiseAll Mode] Expected at least 25 arbs, found {len(r)}" assert multi_carbon_count > 0, f"[NBTest64 TestMultiPairwiseAll Mode] Not finding arbs with multiple Carbon curves." - # assert carbon_wrong_direction_count == 0, f"[NBTest64 TestMultiPairwiseAll Mode] Expected all Carbon curves to have the same tkn in and tkn out. Mixing is currently not supported." + assert carbon_wrong_direction_count == 6, f"[NBTest64 TestMultiPairwiseAll Mode] Expected 6 Carbon curves to be in the opposite direction." # - \ No newline at end of file From 83cccb43cfaba06ebeaab1c1bc11103f1f0a9e05 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Mon, 13 May 2024 11:09:33 +0300 Subject: [PATCH 02/18] Fix function `build_pstart` in class `ArbitrageFinderTriangleBase`, and remove that function in each one of the classes which inherits that class --- fastlane_bot/modes/base_triangle.py | 11 +++++------ fastlane_bot/modes/triangle_multi.py | 18 ------------------ fastlane_bot/modes/triangle_multi_complete.py | 16 ---------------- 3 files changed, 5 insertions(+), 40 deletions(-) diff --git a/fastlane_bot/modes/base_triangle.py b/fastlane_bot/modes/base_triangle.py index 8ce78a7d1..ce07df8bc 100644 --- a/fastlane_bot/modes/base_triangle.py +++ b/fastlane_bot/modes/base_triangle.py @@ -345,18 +345,17 @@ def get_mono_direction_carbon_curves( wrong_direction_cids.append(idx) return [curve for curve in miniverse if curve.cid not in wrong_direction_cids] + def build_pstart(self, CCm, tkn0list, tkn1): tkn0list = [x for x in tkn0list if x not in [tkn1]] pstart = {} for tkn0 in tkn0list: try: - price = CCm.bytknx(tkn0).bytkny(tkn1)[0].p + pstart[tkn0] = CCm.bytknx(tkn0).bytkny(tkn1)[0].p except: try: - price = 1/CCm.bytknx(tkn1).bytkny(tkn0)[0].p + pstart[tkn0] = 1/CCm.bytknx(tkn1).bytkny(tkn0)[0].p except Exception as e: - print(str(e)) - self.ConfigObj.logger.debug(f"[pstart build] {tkn0} not supported. w {tkn1} {str(e)}") - pstart[tkn0]=price + self.ConfigObj.logger.info(f"[pstart build] {tkn0}/{tkn1} price error {e}") pstart[tkn1] = 1 - return pstart \ No newline at end of file + return pstart diff --git a/fastlane_bot/modes/triangle_multi.py b/fastlane_bot/modes/triangle_multi.py index 50e335880..6a0e2d926 100644 --- a/fastlane_bot/modes/triangle_multi.py +++ b/fastlane_bot/modes/triangle_multi.py @@ -92,21 +92,3 @@ def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_p ) return candidates if self.result == self.AO_CANDIDATES else ops - - def build_pstart(self, CCm, tkn0list, tkn1): - tkn0list = [x for x in tkn0list if x not in [tkn1]] - pstart = {} - for tkn0 in tkn0list: - try: - price = CCm.bytknx(tkn0).bytkny(tkn1)[0].p - except: - try: - price = 1/CCm.bytknx(tkn1).bytkny(tkn0)[0].p - except Exception as e: - print(str(e)) - self.ConfigObj.logger.debug(f"[pstart build] {tkn0} not supported. w {tkn1} {str(e)}") - pstart[tkn0]=price - pstart[tkn1] = 1 - return pstart - - diff --git a/fastlane_bot/modes/triangle_multi_complete.py b/fastlane_bot/modes/triangle_multi_complete.py index e2ee8c947..171fe032f 100644 --- a/fastlane_bot/modes/triangle_multi_complete.py +++ b/fastlane_bot/modes/triangle_multi_complete.py @@ -81,19 +81,3 @@ def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_p ) return candidates if self.result == self.AO_CANDIDATES else ops - - def build_pstart(self, CCm, tkn0list, tkn1): - tkn0list = [x for x in tkn0list if x not in [tkn1]] - pstart = {} - for tkn0 in tkn0list: - try: - pstart[tkn0] = CCm.bytknx(tkn0).bytkny(tkn1)[0].p - except: - try: - pstart[tkn0] = 1/CCm.bytknx(tkn1).bytkny(tkn0)[0].p - except Exception as e: - self.ConfigObj.logger.info(f"[pstart build] {tkn0} not supported. w {tkn1} {e}") - pstart[tkn1] = 1 - return pstart - - From e13e85b825de1bc7a16ddfe7b539996d5fe06a51 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Mon, 13 May 2024 11:57:21 +0300 Subject: [PATCH 03/18] Fix the handling of trade instructions in function ArbitrageFinderTriangleMulti.find_arbitrage --- fastlane_bot/modes/triangle_multi.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fastlane_bot/modes/triangle_multi.py b/fastlane_bot/modes/triangle_multi.py index 6a0e2d926..f3a7c5289 100644 --- a/fastlane_bot/modes/triangle_multi.py +++ b/fastlane_bot/modes/triangle_multi.py @@ -42,22 +42,17 @@ def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_p r = None CC_cc = CPCContainer(miniverse) O = MargPOptimizer(CC_cc) - #try: pstart = self.build_pstart(CC_cc, CC_cc.tokens(), src_token) r = O.optimize(src_token, params=dict(pstart=pstart)) #debug=True, debug2=True trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - if len(trade_instructions_dic) < 3: + if trade_instructions_dic is None or len(trade_instructions_dic) < 3: # Failed to converge continue trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) trade_instructions = r.trade_instructions() except Exception as e: - self.ConfigObj.logger.debug(f"[triangle multi] {str(e)}") - continue - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 2: + self.ConfigObj.logger.info(f"[triangle multi] {e}") continue profit_src = -r.result From d3feda568dda513e010afbbf3554f5e3a345ecab Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Mon, 13 May 2024 12:00:52 +0300 Subject: [PATCH 04/18] Remove unused code --- fastlane_bot/modes/triangle_multi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fastlane_bot/modes/triangle_multi.py b/fastlane_bot/modes/triangle_multi.py index f3a7c5289..917260f49 100644 --- a/fastlane_bot/modes/triangle_multi.py +++ b/fastlane_bot/modes/triangle_multi.py @@ -39,7 +39,6 @@ def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_p for src_token, miniverse in combos: try: - r = None CC_cc = CPCContainer(miniverse) O = MargPOptimizer(CC_cc) pstart = self.build_pstart(CC_cc, CC_cc.tokens(), src_token) From cb62b201a76ad1169f71a2893f9c5969630a47e6 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Mon, 13 May 2024 13:10:26 +0300 Subject: [PATCH 05/18] Remove unused code --- fastlane_bot/modes/base_triangle.py | 46 ----------------------------- 1 file changed, 46 deletions(-) diff --git a/fastlane_bot/modes/base_triangle.py b/fastlane_bot/modes/base_triangle.py index ce07df8bc..522283b36 100644 --- a/fastlane_bot/modes/base_triangle.py +++ b/fastlane_bot/modes/base_triangle.py @@ -300,52 +300,6 @@ def get_comprehensive_triangles( combos.extend(flt_triangle_analysis_set) return combos - @staticmethod - def get_mono_direction_carbon_curves( - miniverse: List[Any], trade_instructions_df: pd.DataFrame, token_in: str=None - ) -> List[Any]: - """ - Get mono direction carbon curves for triangular arbitrage - - Parameters - ---------- - miniverse : list - List of miniverses - token_in : str - Token in - trade_instructions_df : DataFrame - Trade instructions dataframe - - Returns - ------- - mono_direction_carbon_curves : list - List of mono direction carbon curves - - """ - - if token_in is None: - columns = trade_instructions_df.columns - check_nan = trade_instructions_df.copy().fillna(0) - first_bancor_v3_pool = check_nan.iloc[0] - second_bancor_v3_pool = check_nan.iloc[1] - - for idx, token in enumerate(columns): - if token == T.BNT: - continue - if first_bancor_v3_pool[token] < 0: - token_in = token - break - if second_bancor_v3_pool[token] < 0: - token_in = token - break - - wrong_direction_cids = [] - for idx, row in trade_instructions_df.iterrows(): - if (row[token_in] < 0) and ("-0" in idx or "-1" in idx): - wrong_direction_cids.append(idx) - - return [curve for curve in miniverse if curve.cid not in wrong_direction_cids] - def build_pstart(self, CCm, tkn0list, tkn1): tkn0list = [x for x in tkn0list if x not in [tkn1]] pstart = {} From 687d4012f18371c58079826ce38a39bd78a6a507 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Mon, 13 May 2024 13:12:14 +0300 Subject: [PATCH 06/18] Cosmetic --- fastlane_bot/modes/triangle_multi.py | 6 ++---- fastlane_bot/modes/triangle_multi_complete.py | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/fastlane_bot/modes/triangle_multi.py b/fastlane_bot/modes/triangle_multi.py index 917260f49..e6d1f6672 100644 --- a/fastlane_bot/modes/triangle_multi.py +++ b/fastlane_bot/modes/triangle_multi.py @@ -33,16 +33,14 @@ def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_p if candidates is None: candidates = [] - combos = self.get_combos( - self.flashloan_tokens, self.CCm, arb_mode=self.arb_mode - ) + combos = self.get_combos(self.flashloan_tokens, self.CCm, arb_mode=self.arb_mode) for src_token, miniverse in combos: try: CC_cc = CPCContainer(miniverse) O = MargPOptimizer(CC_cc) pstart = self.build_pstart(CC_cc, CC_cc.tokens(), src_token) - r = O.optimize(src_token, params=dict(pstart=pstart)) #debug=True, debug2=True + r = O.optimize(src_token, params=dict(pstart=pstart)) trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) if trade_instructions_dic is None or len(trade_instructions_dic) < 3: # Failed to converge diff --git a/fastlane_bot/modes/triangle_multi_complete.py b/fastlane_bot/modes/triangle_multi_complete.py index 171fe032f..d053ae0fc 100644 --- a/fastlane_bot/modes/triangle_multi_complete.py +++ b/fastlane_bot/modes/triangle_multi_complete.py @@ -37,7 +37,7 @@ def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_p CC_cc = CPCContainer(miniverse) O = MargPOptimizer(CC_cc) pstart = self.build_pstart(CC_cc, CC_cc.tokens(), src_token) - r = O.optimize(src_token, params=dict(pstart=pstart)) #debug=True, debug2=True, verbose=True + r = O.optimize(src_token, params=dict(pstart=pstart)) trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) if trade_instructions_dic is None or len(trade_instructions_dic) < 3: # Failed to converge From 679b983c5abcc65144f9be425dd4cf9fdfe23317 Mon Sep 17 00:00:00 2001 From: Platon Floria Date: Mon, 13 May 2024 11:26:43 +0300 Subject: [PATCH 07/18] fix: type annotation --- fastlane_bot/config/multicaller.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fastlane_bot/config/multicaller.py b/fastlane_bot/config/multicaller.py index d68760942..76b031bf6 100644 --- a/fastlane_bot/config/multicaller.py +++ b/fastlane_bot/config/multicaller.py @@ -6,9 +6,10 @@ All rights reserved. Licensed under MIT. """ -from typing import Callable, Any, List, Dict +from typing import Any, List, Dict from eth_abi import decode +from web3.contract.contract import ContractFunction from fastlane_bot.data.abi import MULTICALL_ABI @@ -45,10 +46,10 @@ class MultiCaller: def __init__(self, web3: Any, multicall_contract_address: str): self.multicall_contract = web3.eth.contract(abi=MULTICALL_ABI, address=multicall_contract_address) - self.contract_calls: List[Callable] = [] + self.contract_calls: List[ContractFunction] = [] self.output_types_list: List[List[str]] = [] - def add_call(self, call: Callable): + def add_call(self, call: ContractFunction): self.contract_calls.append({'target': call.address, 'callData': call._encode_transaction_data()}) self.output_types_list.append([collapse_if_tuple(item) for item in call.abi['outputs']]) From 1e3dec405bdaa0ec9ef95241fe6e4c2d3f34de4d Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 12:58:56 +0300 Subject: [PATCH 08/18] Fix the pool-finder-period handling --- main.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/main.py b/main.py index b4d7d2739..d3b8bc5ed 100644 --- a/main.py +++ b/main.py @@ -94,6 +94,7 @@ def int_or_none(x): "self_fund": is_true, "read_only": is_true, "is_args_test": is_true, + "pool_finder_period": int, } # Apply the transformations @@ -225,7 +226,7 @@ def main(args: argparse.Namespace) -> None: prefix_path: {args.prefix_path} self_fund: {args.self_fund} read_only: {args.read_only} - pool_finder: {args.pool_finder} + pool_finder_period: {args.pool_finder_period} +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -306,17 +307,14 @@ def run(mgr, args, tenderly_uri=None) -> None: event_gatherer = EventGatherer(w3=mgr.w3_async, exchanges=mgr.exchanges, event_contracts=mgr.event_contracts) - if args.pool_finder != -1: - pool_finder = PoolFinder( - carbon_forks=mgr.cfg.network.CARBON_V1_FORKS, - uni_v3_forks=mgr.cfg.network.UNI_V3_FORKS, - flashloan_tokens=args.flashloan_tokens, - exchanges=mgr.exchanges, - web3=mgr.web3, - multicall_address=mgr.cfg.network.MULTICALL_CONTRACT_ADDRESS - ) - else: - pool_finder = None + pool_finder = PoolFinder( + carbon_forks=mgr.cfg.network.CARBON_V1_FORKS, + uni_v3_forks=mgr.cfg.network.UNI_V3_FORKS, + flashloan_tokens=args.flashloan_tokens, + exchanges=mgr.exchanges, + web3=mgr.web3, + multicall_address=mgr.cfg.network.MULTICALL_CONTRACT_ADDRESS + ) while True: try: @@ -527,7 +525,7 @@ def run(mgr, args, tenderly_uri=None) -> None: mgr.solidly_v2_event_mappings = dict( solidly_v2_event_mappings[["address", "exchange"]].values ) - if pool_finder is not None and (loop_idx % args.pool_finder == 0 or loop_idx == 1): + if args.pool_finder_period > 0 and loop_idx % args.pool_finder_period == 0: mgr.cfg.logger.info(f"Searching for unsupported Carbon pairs.") uni_v2, uni_v3, solidly_v2 = pool_finder.get_pools_for_unsupported_pairs(mgr.pool_data, arb_mode=args.arb_mode) result = f"Added {len(uni_v2) + len(uni_v3) + len(solidly_v2)} pools." if (uni_v2 or uni_v3 or solidly_v2) else f"No pools added." @@ -735,9 +733,9 @@ def run(mgr, args, tenderly_uri=None) -> None: help="Custom RPC URL. If not set, the bot will use the default Alchemy RPC URL for the blockchain (if available).", ) parser.add_argument( - "--pool_finder", + "--pool_finder_period", default=100, - help="If not -1, searches for pools that can service Carbon strategies that do not have viable routes.", + help="Searches for pools that can service Carbon strategies that do not have viable routes.", ) # Process the arguments From d5e251ed34fc2de64675be56b41cc7e208033c6e Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 15:32:50 +0300 Subject: [PATCH 09/18] Remove the multicall dependency from all Exchange classes --- fastlane_bot/events/exchanges/balancer.py | 7 ------- fastlane_bot/events/exchanges/bancor_pol.py | 7 ------- fastlane_bot/events/exchanges/bancor_v2.py | 10 +--------- fastlane_bot/events/exchanges/bancor_v3.py | 9 +-------- fastlane_bot/events/exchanges/base.py | 8 -------- fastlane_bot/events/exchanges/carbon_v1.py | 7 ------- .../events/exchanges/solidly_v2/base.py | 5 ++--- .../events/exchanges/solidly_v2/velodrome_v2.py | 5 ++--- .../events/exchanges/solidly_v2/xfai_v0.py | 5 ++--- fastlane_bot/events/exchanges/uniswap_v2.py | 5 ++--- fastlane_bot/events/exchanges/uniswap_v3.py | 7 +++---- fastlane_bot/pool_finder.py | 17 +++++------------ main.py | 3 +-- 13 files changed, 19 insertions(+), 76 deletions(-) diff --git a/fastlane_bot/events/exchanges/balancer.py b/fastlane_bot/events/exchanges/balancer.py index 2f9f99fc9..2ec32119b 100644 --- a/fastlane_bot/events/exchanges/balancer.py +++ b/fastlane_bot/events/exchanges/balancer.py @@ -16,7 +16,6 @@ from web3.contract import Contract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import BALANCER_VAULT_ABI, BALANCER_POOL_ABI_V1 from ..exchanges.base import Exchange from ..pools.base import Pool @@ -89,9 +88,3 @@ async def get_tkn_n(self, address: str, contract: Contract, event: Any, index: i tokens = pool_balances[0] token_balances = pool_balances[1] return token_balances[index] - - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - """ - This function is unused for Carbon. - """ - raise NotImplementedError \ No newline at end of file diff --git a/fastlane_bot/events/exchanges/bancor_pol.py b/fastlane_bot/events/exchanges/bancor_pol.py index 3fbb0fbb0..bc66bb7c5 100644 --- a/fastlane_bot/events/exchanges/bancor_pol.py +++ b/fastlane_bot/events/exchanges/bancor_pol.py @@ -16,7 +16,6 @@ from web3.contract import Contract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import BANCOR_POL_ABI from fastlane_bot import Config from ..exchanges.base import Exchange @@ -64,12 +63,6 @@ async def get_tkn0(self, address: str, contract: Contract, event: Event) -> str: async def get_tkn1(self, address: str, contract: Contract, event: Event) -> str: return self.ETH_ADDRESS if event.args["token"] not in self.ETH_ADDRESS else self.BNT_ADDRESS - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - """ - This function is unused for Carbon. - """ - raise NotImplementedError - def save_strategy( self, token: str, diff --git a/fastlane_bot/events/exchanges/bancor_v2.py b/fastlane_bot/events/exchanges/bancor_v2.py index 41ff63860..8da7d1745 100644 --- a/fastlane_bot/events/exchanges/bancor_v2.py +++ b/fastlane_bot/events/exchanges/bancor_v2.py @@ -12,12 +12,10 @@ Licensed under MIT. """ from dataclasses import dataclass -from typing import List, Type, Tuple, Any +from typing import List, Type, Tuple -from web3 import AsyncWeb3 from web3.contract import Contract, AsyncContract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import BANCOR_V2_CONVERTER_ABI from ..exchanges.base import Exchange from ..pools.base import Pool @@ -78,9 +76,3 @@ async def get_tkn1(self, address: str, contract: Contract, event: Event) -> str: async def get_anchor(self, contract: Contract) -> str: return await contract.caller.anchor() - - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - """ - This function is unused for Carbon. - """ - raise NotImplementedError \ No newline at end of file diff --git a/fastlane_bot/events/exchanges/bancor_v3.py b/fastlane_bot/events/exchanges/bancor_v3.py index b21c6f7bb..2af08a103 100644 --- a/fastlane_bot/events/exchanges/bancor_v3.py +++ b/fastlane_bot/events/exchanges/bancor_v3.py @@ -12,11 +12,10 @@ Licensed under MIT. """ from dataclasses import dataclass -from typing import List, Type, Tuple, Any +from typing import List, Type, Tuple from web3.contract import Contract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import BANCOR_V3_POOL_COLLECTION_ABI from ..exchanges.base import Exchange from ..pools.base import Pool @@ -65,9 +64,3 @@ async def get_tkn1(self, address: str, contract: Contract, event: Event) -> str: if event.args["pool"] != self.BNT_ADDRESS else event.args["tkn_address"] ) - - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - """ - This function is unused for Carbon. - """ - raise NotImplementedError \ No newline at end of file diff --git a/fastlane_bot/events/exchanges/base.py b/fastlane_bot/events/exchanges/base.py index 93020c3b5..bf450ff12 100644 --- a/fastlane_bot/events/exchanges/base.py +++ b/fastlane_bot/events/exchanges/base.py @@ -17,7 +17,6 @@ from web3.contract import Contract, AsyncContract from fastlane_bot.config.constants import CARBON_V1_NAME -from fastlane_bot.config.multicaller import MultiCaller from ..pools.base import Pool from ..interfaces.subscription import Subscription @@ -124,13 +123,6 @@ async def get_fee(address: str, contract: AsyncContract) -> float: """ pass - @abstractmethod - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2, *args): - """ - Returns the Factory contract function used to fetch liquidity pools. - """ - ... - @staticmethod @abstractmethod async def get_tkn0(address: str, contract: AsyncContract, event: Any) -> str: diff --git a/fastlane_bot/events/exchanges/carbon_v1.py b/fastlane_bot/events/exchanges/carbon_v1.py index b3708c822..df0540661 100644 --- a/fastlane_bot/events/exchanges/carbon_v1.py +++ b/fastlane_bot/events/exchanges/carbon_v1.py @@ -17,7 +17,6 @@ from fastlane_bot import Config from web3.contract import Contract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import CARBON_CONTROLLER_ABI from ..exchanges.base import Exchange from ..pools.base import Pool @@ -245,9 +244,3 @@ def save_strategy( ), block_number=block_number, ) - - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - """ - This function is unused for Carbon. - """ - raise NotImplementedError \ No newline at end of file diff --git a/fastlane_bot/events/exchanges/solidly_v2/base.py b/fastlane_bot/events/exchanges/solidly_v2/base.py index 52a971e7e..e58a474ff 100644 --- a/fastlane_bot/events/exchanges/solidly_v2/base.py +++ b/fastlane_bot/events/exchanges/solidly_v2/base.py @@ -17,7 +17,6 @@ from web3.contract import Contract, AsyncContract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import SOLIDLY_V2_POOL_ABI from fastlane_bot.events.exchanges.base import Exchange from ...exchanges.base import Exchange @@ -63,8 +62,8 @@ async def get_tkn0(self, address: str, contract: Contract, event: Any) -> str: async def get_tkn1(self, address: str, contract: Contract, event: Any) -> str: return await contract.caller.token1() - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - mc.add_call(self.factory_contract.functions.getPair, addr1, addr2, True) + def get_pool(self, addr1, addr2): + return self.factory_contract.functions.getPair(addr1, addr2, False) @property @abstractmethod diff --git a/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py b/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py index 1224c6df6..e15d17bf2 100644 --- a/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py +++ b/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py @@ -3,7 +3,6 @@ from web3.contract import AsyncContract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import SOLIDLY_V2_FACTORY_ABI from .base import SolidlyV2 @@ -19,5 +18,5 @@ async def get_fee(self, address: str, contract: AsyncContract) -> Tuple[str, flo fee_float = float(fee) / 10 ** 4 return str(fee_float), fee_float - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - mc.add_call(self.factory_contract.functions.getPool, addr1, addr2, True) + def get_pool(self, addr1, addr2): + return self.factory_contract.functions.getPool(addr1, addr2, False) diff --git a/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py b/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py index e8d7eb55e..75cc7cb00 100644 --- a/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py +++ b/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py @@ -3,7 +3,6 @@ from web3.contract import Contract, AsyncContract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import XFAI_V0_POOL_ABI, XFAI_V0_FACTORY_ABI from .base import SolidlyV2 @@ -30,5 +29,5 @@ async def get_fee(self, address: str, contract: AsyncContract) -> Tuple[str, flo fee_float = float(fee) / 10 ** 4 return str(fee_float), fee_float - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - mc.add_call(self.factory_contract.functions.getPool, addr1) + def get_pool(self, addr1, addr2): + return self.factory_contract.functions.getPool(addr1) diff --git a/fastlane_bot/events/exchanges/uniswap_v2.py b/fastlane_bot/events/exchanges/uniswap_v2.py index c7ed2615f..1043f7afe 100644 --- a/fastlane_bot/events/exchanges/uniswap_v2.py +++ b/fastlane_bot/events/exchanges/uniswap_v2.py @@ -16,7 +16,6 @@ from web3.contract import Contract, AsyncContract -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import UNISWAP_V2_POOL_ABI, UNISWAP_V2_FACTORY_ABI from ..exchanges.base import Exchange from ..pools.base import Pool @@ -64,5 +63,5 @@ async def get_tkn0(address: str, contract: AsyncContract, event: Any) -> str: async def get_tkn1(address: str, contract: AsyncContract, event: Any) -> str: return await contract.caller.token1() - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2): - mc.add_call(self.factory_contract.functions.getPair, addr1, addr2) + def get_pool(self, addr1, addr2): + return self.factory_contract.functions.getPair(addr1, addr2) diff --git a/fastlane_bot/events/exchanges/uniswap_v3.py b/fastlane_bot/events/exchanges/uniswap_v3.py index 604ba5af9..569fd6bee 100644 --- a/fastlane_bot/events/exchanges/uniswap_v3.py +++ b/fastlane_bot/events/exchanges/uniswap_v3.py @@ -14,10 +14,9 @@ from dataclasses import dataclass from typing import List, Type, Tuple, Any -from web3.contract import Contract, AsyncContract +from web3.contract import Contract from fastlane_bot.config.constants import AGNI_V3_NAME, PANCAKESWAP_V3_NAME, FUSIONX_V3_NAME, ECHODEX_V3_NAME, SECTA_V3_NAME -from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.data.abi import UNISWAP_V3_POOL_ABI, UNISWAP_V3_FACTORY_ABI, PANCAKESWAP_V3_POOL_ABI from ..exchanges.base import Exchange from ..pools.base import Pool @@ -61,5 +60,5 @@ async def get_tkn0(self, address: str, contract: Contract, event: Any) -> str: async def get_tkn1(self, address: str, contract: Contract, event: Any) -> str: return await contract.caller.token1() - def get_pool_with_multicall(self, mc: MultiCaller, addr1, addr2, fee): - mc.add_call(self.factory_contract.functions.getPool, addr1, addr2, fee) + def get_pool(self, addr1, addr2, fee): + return self.factory_contract.functions.getPool(addr1, addr2, fee) diff --git a/fastlane_bot/pool_finder.py b/fastlane_bot/pool_finder.py index 8f56436e4..988e2e7da 100644 --- a/fastlane_bot/pool_finder.py +++ b/fastlane_bot/pool_finder.py @@ -91,11 +91,6 @@ def _find_pools(self, unsupported_pairs: List[Tuple]) -> Dict[str, List[str]]: Dict[str, List[str]]: A list of dictionaries, where each dictionary maps an exchange's name to a list of valid pool addresses (i.e., non-zero addresses) obtained from the multicall across all generated pairs. - - Raises: - Exception: An exception could be raised from the multicall operation depending on the - implementation specifics of the multicall context manager or the exchange's - get_pool_with_multicall method if it encounters a problem. """ pairs = [(tkn, token) for pair in unsupported_pairs for tkn in pair for token in self._flashloan_tokens] chunk_size = 400 @@ -108,17 +103,15 @@ def _find_pools(self, unsupported_pairs: List[Tuple]) -> Dict[str, List[str]]: mc = MultiCaller(contract=exchange.factory_contract, web3=self._web3, multicall_address=self._multicall_address) for pair in pair_chunk: if exchange.base_exchange_name in [UNISWAP_V2_NAME, SOLIDLY_V2_NAME]: - exchange.get_pool_with_multicall(mc, pair[0], pair[1]) + mc.add_call(exchange.get_pool(pair[0], pair[1])) elif exchange.base_exchange_name == UNISWAP_V3_NAME: for fee in self._uni_v3_fee_tiers[exchange.exchange_name]: - exchange.get_pool_with_multicall(mc, pair[0], pair[1], fee) + mc.add_call(exchange.get_pool(pair[0], pair[1], fee)) response = mc.multicall() - result[exchange.base_exchange_name] = { + result[exchange.base_exchange_name].update({ mc.web3.to_checksum_address(addr): exchange.exchange_name - for addr - in response - if addr != ZERO_ADDRESS - } + for addr in response if addr != ZERO_ADDRESS + }) return result diff --git a/main.py b/main.py index d3b8bc5ed..f5187f222 100644 --- a/main.py +++ b/main.py @@ -528,8 +528,7 @@ def run(mgr, args, tenderly_uri=None) -> None: if args.pool_finder_period > 0 and loop_idx % args.pool_finder_period == 0: mgr.cfg.logger.info(f"Searching for unsupported Carbon pairs.") uni_v2, uni_v3, solidly_v2 = pool_finder.get_pools_for_unsupported_pairs(mgr.pool_data, arb_mode=args.arb_mode) - result = f"Added {len(uni_v2) + len(uni_v3) + len(solidly_v2)} pools." if (uni_v2 or uni_v3 or solidly_v2) else f"No pools added." - mgr.cfg.logger.info(result) + mgr.cfg.logger.info(f"Number of pools added: {len(uni_v2) + len(uni_v3) + len(solidly_v2)}") mgr.uniswap_v2_event_mappings.update(uni_v2) mgr.uniswap_v3_event_mappings.update(uni_v3) mgr.solidly_v2_event_mappings.update(solidly_v2) From b810e56837126fc9d8af8294472532e7c21ba981 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 15:39:08 +0300 Subject: [PATCH 10/18] Minor --- fastlane_bot/pool_finder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane_bot/pool_finder.py b/fastlane_bot/pool_finder.py index 988e2e7da..fa71809ba 100644 --- a/fastlane_bot/pool_finder.py +++ b/fastlane_bot/pool_finder.py @@ -109,7 +109,7 @@ def _find_pools(self, unsupported_pairs: List[Tuple]) -> Dict[str, List[str]]: mc.add_call(exchange.get_pool(pair[0], pair[1], fee)) response = mc.multicall() result[exchange.base_exchange_name].update({ - mc.web3.to_checksum_address(addr): exchange.exchange_name + self._web3.to_checksum_address(addr): exchange.exchange_name for addr in response if addr != ZERO_ADDRESS }) return result From e7b88cbe87b4af5d84e12a816fc6544ed1e171bb Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 15:47:28 +0300 Subject: [PATCH 11/18] Simplify the code --- fastlane_bot/pool_finder.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/fastlane_bot/pool_finder.py b/fastlane_bot/pool_finder.py index fa71809ba..606c12ef5 100644 --- a/fastlane_bot/pool_finder.py +++ b/fastlane_bot/pool_finder.py @@ -61,7 +61,7 @@ def get_pools_for_unsupported_pairs(self, pools: List[Dict[str, Any]], arb_mode: _find_unsupported_pairs, and _find_unsupported_triangles methods. Returns: - Dict: Returns a dictionary with pools sorted into different exchange types (Uni V2 forks, Uni V3 forks, + Dict: Returns a list of dictionaries with pools sorted into different exchange types (Uni V2 forks, Uni V3 forks, and Solidly V2 forks), each associated with their specific supporting pools based on the unsupported configurations identified. """ @@ -73,25 +73,7 @@ def get_pools_for_unsupported_pairs(self, pools: List[Dict[str, Any]], arb_mode: unsupported_pairs = PoolFinder._find_unsupported_triangles(self._flashloan_tokens, carbon_pairs=carbon_pairs, external_pairs=other_pairs) else: unsupported_pairs = PoolFinder._find_unsupported_pairs(self._flashloan_tokens, carbon_pairs=carbon_pairs, external_pairs=other_pairs) - missing_pools = self._find_pools(unsupported_pairs) - return missing_pools[UNISWAP_V2_NAME], missing_pools[UNISWAP_V3_NAME], missing_pools[SOLIDLY_V2_NAME] - - def _find_pools(self, unsupported_pairs: List[Tuple]) -> Dict[str, List[str]]: - """ - Collects pool addresses for each exchange, based on a set of unsupported token pairs - and flashloan tokens. The function constructs pairs of tokens from unsupported_pairs with each - flashloan token and retrieves pool data via multicall. It filters out invalid addresses. - - Args: - unsupported_pairs (List[Tuple]): A list of tuples, where each tuple contains two token addresses. - flashloan_tokens (List[str]): A list of token addresses available for flashloans. - - Returns: - Dict[str, List[str]]: A list of dictionaries, where each dictionary maps an exchange's name - to a list of valid pool addresses (i.e., non-zero addresses) obtained - from the multicall across all generated pairs. - """ pairs = [(tkn, token) for pair in unsupported_pairs for tkn in pair for token in self._flashloan_tokens] chunk_size = 400 # Create the list of chunks @@ -112,7 +94,8 @@ def _find_pools(self, unsupported_pairs: List[Tuple]) -> Dict[str, List[str]]: self._web3.to_checksum_address(addr): exchange.exchange_name for addr in response if addr != ZERO_ADDRESS }) - return result + + return result[UNISWAP_V2_NAME], result[UNISWAP_V3_NAME], result[SOLIDLY_V2_NAME] def _extract_pairs(self, pools: List[Dict[str, Any]]) -> Tuple[List, set]: From 546720e5779da71786d70102650a8a86388dad39 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 15:54:34 +0300 Subject: [PATCH 12/18] Rename `get_pool` to `get_pool_func_call` --- fastlane_bot/events/exchanges/solidly_v2/base.py | 2 +- fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py | 2 +- fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py | 2 +- fastlane_bot/events/exchanges/uniswap_v2.py | 2 +- fastlane_bot/events/exchanges/uniswap_v3.py | 2 +- fastlane_bot/pool_finder.py | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fastlane_bot/events/exchanges/solidly_v2/base.py b/fastlane_bot/events/exchanges/solidly_v2/base.py index e58a474ff..7bffef541 100644 --- a/fastlane_bot/events/exchanges/solidly_v2/base.py +++ b/fastlane_bot/events/exchanges/solidly_v2/base.py @@ -62,7 +62,7 @@ async def get_tkn0(self, address: str, contract: Contract, event: Any) -> str: async def get_tkn1(self, address: str, contract: Contract, event: Any) -> str: return await contract.caller.token1() - def get_pool(self, addr1, addr2): + def get_pool_func_call(self, addr1, addr2): return self.factory_contract.functions.getPair(addr1, addr2, False) @property diff --git a/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py b/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py index e15d17bf2..27dc61bd4 100644 --- a/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py +++ b/fastlane_bot/events/exchanges/solidly_v2/velodrome_v2.py @@ -18,5 +18,5 @@ async def get_fee(self, address: str, contract: AsyncContract) -> Tuple[str, flo fee_float = float(fee) / 10 ** 4 return str(fee_float), fee_float - def get_pool(self, addr1, addr2): + def get_pool_func_call(self, addr1, addr2): return self.factory_contract.functions.getPool(addr1, addr2, False) diff --git a/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py b/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py index 75cc7cb00..2b40d98d3 100644 --- a/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py +++ b/fastlane_bot/events/exchanges/solidly_v2/xfai_v0.py @@ -29,5 +29,5 @@ async def get_fee(self, address: str, contract: AsyncContract) -> Tuple[str, flo fee_float = float(fee) / 10 ** 4 return str(fee_float), fee_float - def get_pool(self, addr1, addr2): + def get_pool_func_call(self, addr1, addr2): return self.factory_contract.functions.getPool(addr1) diff --git a/fastlane_bot/events/exchanges/uniswap_v2.py b/fastlane_bot/events/exchanges/uniswap_v2.py index 1043f7afe..ce1e209cc 100644 --- a/fastlane_bot/events/exchanges/uniswap_v2.py +++ b/fastlane_bot/events/exchanges/uniswap_v2.py @@ -63,5 +63,5 @@ async def get_tkn0(address: str, contract: AsyncContract, event: Any) -> str: async def get_tkn1(address: str, contract: AsyncContract, event: Any) -> str: return await contract.caller.token1() - def get_pool(self, addr1, addr2): + def get_pool_func_call(self, addr1, addr2): return self.factory_contract.functions.getPair(addr1, addr2) diff --git a/fastlane_bot/events/exchanges/uniswap_v3.py b/fastlane_bot/events/exchanges/uniswap_v3.py index 569fd6bee..76d8a93d5 100644 --- a/fastlane_bot/events/exchanges/uniswap_v3.py +++ b/fastlane_bot/events/exchanges/uniswap_v3.py @@ -60,5 +60,5 @@ async def get_tkn0(self, address: str, contract: Contract, event: Any) -> str: async def get_tkn1(self, address: str, contract: Contract, event: Any) -> str: return await contract.caller.token1() - def get_pool(self, addr1, addr2, fee): + def get_pool_func_call(self, addr1, addr2, fee): return self.factory_contract.functions.getPool(addr1, addr2, fee) diff --git a/fastlane_bot/pool_finder.py b/fastlane_bot/pool_finder.py index 606c12ef5..2cdb8df7f 100644 --- a/fastlane_bot/pool_finder.py +++ b/fastlane_bot/pool_finder.py @@ -85,10 +85,10 @@ def get_pools_for_unsupported_pairs(self, pools: List[Dict[str, Any]], arb_mode: mc = MultiCaller(contract=exchange.factory_contract, web3=self._web3, multicall_address=self._multicall_address) for pair in pair_chunk: if exchange.base_exchange_name in [UNISWAP_V2_NAME, SOLIDLY_V2_NAME]: - mc.add_call(exchange.get_pool(pair[0], pair[1])) + mc.add_call(exchange.get_pool_func_call(pair[0], pair[1])) elif exchange.base_exchange_name == UNISWAP_V3_NAME: for fee in self._uni_v3_fee_tiers[exchange.exchange_name]: - mc.add_call(exchange.get_pool(pair[0], pair[1], fee)) + mc.add_call(exchange.get_pool_func_call(pair[0], pair[1], fee)) response = mc.multicall() result[exchange.base_exchange_name].update({ self._web3.to_checksum_address(addr): exchange.exchange_name From d13393e6fd6c7b1ca2529def9d92d6109572d10e Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 15:57:48 +0300 Subject: [PATCH 13/18] Semantic --- fastlane_bot/pool_finder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane_bot/pool_finder.py b/fastlane_bot/pool_finder.py index 2cdb8df7f..5e047a026 100644 --- a/fastlane_bot/pool_finder.py +++ b/fastlane_bot/pool_finder.py @@ -89,10 +89,10 @@ def get_pools_for_unsupported_pairs(self, pools: List[Dict[str, Any]], arb_mode: elif exchange.base_exchange_name == UNISWAP_V3_NAME: for fee in self._uni_v3_fee_tiers[exchange.exchange_name]: mc.add_call(exchange.get_pool_func_call(pair[0], pair[1], fee)) - response = mc.multicall() + response = mc.multicall() # TODO: Handle failures gracefully once supported in class `MultiCaller` result[exchange.base_exchange_name].update({ - self._web3.to_checksum_address(addr): exchange.exchange_name - for addr in response if addr != ZERO_ADDRESS + self._web3.to_checksum_address(address): exchange.exchange_name + for address in response if address != ZERO_ADDRESS }) return result[UNISWAP_V2_NAME], result[UNISWAP_V3_NAME], result[SOLIDLY_V2_NAME] From ba9075c7b01bf07c2cd9a3eb63a0ca91700399eb Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 19:06:44 +0300 Subject: [PATCH 14/18] Cleanup --- fastlane_bot/data/abi.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fastlane_bot/data/abi.py b/fastlane_bot/data/abi.py index daaa04a03..ffdcf65c5 100644 --- a/fastlane_bot/data/abi.py +++ b/fastlane_bot/data/abi.py @@ -169,10 +169,8 @@ "type": "function", "name": "getPair", "stateMutability": "view", - "constant": True, "inputs": [{"internalType": "address", "name": "", "type": "address"}, {"internalType": "address", "name": "", "type": "address"}], - "outputs": [{"internalType": "address", "name": "", "type": "address"}], - "payable": False, + "outputs": [{"internalType": "address", "name": "", "type": "address"}] } ] @@ -256,9 +254,7 @@ "type": "function", "name": "getPair", "stateMutability": "view", - "inputs": [{"internalType": "address", "name": "", "type": "address"}, - {"internalType": "address", "name": "", "type": "address"}, - {"internalType": "bool", "name": "", "type": "bool"}], + "inputs": [{"internalType": "address", "name": "", "type": "address"}, {"internalType": "address", "name": "", "type": "address"}, {"internalType": "bool", "name": "", "type": "bool"}], "outputs": [{"internalType": "address", "name": "", "type": "address"}] } ] From 1248a500d9ea668c1b270224a778e522f55dbc6c Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 19:37:33 +0300 Subject: [PATCH 15/18] Minor --- fastlane_bot/data/abi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane_bot/data/abi.py b/fastlane_bot/data/abi.py index ffdcf65c5..3c98f7000 100644 --- a/fastlane_bot/data/abi.py +++ b/fastlane_bot/data/abi.py @@ -205,9 +205,9 @@ "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}] }, { + "type": "function", "name": "getPool", "stateMutability": "view", - "type": "function", "inputs": [{"internalType": "address", "name": "tokenA", "type": "address"}, {"internalType": "address", "name": "tokenB", "type": "address"}, {"internalType": "bool", "name": "stable", "type": "bool"}], "outputs": [{"internalType": "address", "name": "", "type": "address"}], } From b4319b16c2b7b648beb8f91ae329c02c7fc87485 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Sun, 12 May 2024 23:22:08 +0300 Subject: [PATCH 16/18] Fix usage of `MultiCall` in `PoolFinder` --- fastlane_bot/pool_finder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastlane_bot/pool_finder.py b/fastlane_bot/pool_finder.py index 5e047a026..15ff0584f 100644 --- a/fastlane_bot/pool_finder.py +++ b/fastlane_bot/pool_finder.py @@ -82,17 +82,17 @@ def get_pools_for_unsupported_pairs(self, pools: List[Dict[str, Any]], arb_mode: for exchange in self._exchanges: for pair_chunk in chunked_pairs: - mc = MultiCaller(contract=exchange.factory_contract, web3=self._web3, multicall_address=self._multicall_address) + mc = MultiCaller(self._web3, self._multicall_address) for pair in pair_chunk: if exchange.base_exchange_name in [UNISWAP_V2_NAME, SOLIDLY_V2_NAME]: mc.add_call(exchange.get_pool_func_call(pair[0], pair[1])) elif exchange.base_exchange_name == UNISWAP_V3_NAME: for fee in self._uni_v3_fee_tiers[exchange.exchange_name]: mc.add_call(exchange.get_pool_func_call(pair[0], pair[1], fee)) - response = mc.multicall() # TODO: Handle failures gracefully once supported in class `MultiCaller` + addresses = mc.run_calls() result[exchange.base_exchange_name].update({ self._web3.to_checksum_address(address): exchange.exchange_name - for address in response if address != ZERO_ADDRESS + for address in addresses if address not in [None, ZERO_ADDRESS] }) return result[UNISWAP_V2_NAME], result[UNISWAP_V3_NAME], result[SOLIDLY_V2_NAME] From 75d57a1c14a59bf184d6e54b6738f4448f2d1c85 Mon Sep 17 00:00:00 2001 From: barak manos <> Date: Mon, 13 May 2024 11:14:02 +0300 Subject: [PATCH 17/18] Ensure that the pool-finder runs on the first iteration when enabled (i.e., when its period is larger than 0) --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index f5187f222..12781b042 100644 --- a/main.py +++ b/main.py @@ -525,7 +525,7 @@ def run(mgr, args, tenderly_uri=None) -> None: mgr.solidly_v2_event_mappings = dict( solidly_v2_event_mappings[["address", "exchange"]].values ) - if args.pool_finder_period > 0 and loop_idx % args.pool_finder_period == 0: + if args.pool_finder_period > 0 and (loop_idx - 1) % args.pool_finder_period == 0: mgr.cfg.logger.info(f"Searching for unsupported Carbon pairs.") uni_v2, uni_v3, solidly_v2 = pool_finder.get_pools_for_unsupported_pairs(mgr.pool_data, arb_mode=args.arb_mode) mgr.cfg.logger.info(f"Number of pools added: {len(uni_v2) + len(uni_v3) + len(solidly_v2)}") From fc94895be2c77a30510541f201a9e6cf5b285ed2 Mon Sep 17 00:00:00 2001 From: Platon Floria Date: Mon, 13 May 2024 14:45:38 +0300 Subject: [PATCH 18/18] chore: added get_pool_func_call to the interface --- fastlane_bot/events/exchanges/balancer.py | 3 +++ fastlane_bot/events/exchanges/bancor_pol.py | 3 +++ fastlane_bot/events/exchanges/bancor_v2.py | 3 +++ fastlane_bot/events/exchanges/bancor_v3.py | 3 +++ fastlane_bot/events/exchanges/base.py | 4 ++++ fastlane_bot/events/exchanges/carbon_v1.py | 3 +++ 6 files changed, 19 insertions(+) diff --git a/fastlane_bot/events/exchanges/balancer.py b/fastlane_bot/events/exchanges/balancer.py index 2ec32119b..384ee5fda 100644 --- a/fastlane_bot/events/exchanges/balancer.py +++ b/fastlane_bot/events/exchanges/balancer.py @@ -88,3 +88,6 @@ async def get_tkn_n(self, address: str, contract: Contract, event: Any, index: i tokens = pool_balances[0] token_balances = pool_balances[1] return token_balances[index] + + def get_pool_func_call(self, addr1, addr2): + raise NotImplementedError diff --git a/fastlane_bot/events/exchanges/bancor_pol.py b/fastlane_bot/events/exchanges/bancor_pol.py index bc66bb7c5..1dab4c8fe 100644 --- a/fastlane_bot/events/exchanges/bancor_pol.py +++ b/fastlane_bot/events/exchanges/bancor_pol.py @@ -105,3 +105,6 @@ def save_strategy( cid=cid, block_number=block_number, ) + + def get_pool_func_call(self, addr1, addr2): + raise NotImplementedError diff --git a/fastlane_bot/events/exchanges/bancor_v2.py b/fastlane_bot/events/exchanges/bancor_v2.py index 8da7d1745..a4ab5db4a 100644 --- a/fastlane_bot/events/exchanges/bancor_v2.py +++ b/fastlane_bot/events/exchanges/bancor_v2.py @@ -76,3 +76,6 @@ async def get_tkn1(self, address: str, contract: Contract, event: Event) -> str: async def get_anchor(self, contract: Contract) -> str: return await contract.caller.anchor() + + def get_pool_func_call(self, addr1, addr2): + raise NotImplementedError diff --git a/fastlane_bot/events/exchanges/bancor_v3.py b/fastlane_bot/events/exchanges/bancor_v3.py index 2af08a103..2b14afe0d 100644 --- a/fastlane_bot/events/exchanges/bancor_v3.py +++ b/fastlane_bot/events/exchanges/bancor_v3.py @@ -64,3 +64,6 @@ async def get_tkn1(self, address: str, contract: Contract, event: Event) -> str: if event.args["pool"] != self.BNT_ADDRESS else event.args["tkn_address"] ) + + def get_pool_func_call(self, addr1, addr2): + raise NotImplementedError diff --git a/fastlane_bot/events/exchanges/base.py b/fastlane_bot/events/exchanges/base.py index bf450ff12..8ff438a1b 100644 --- a/fastlane_bot/events/exchanges/base.py +++ b/fastlane_bot/events/exchanges/base.py @@ -123,6 +123,10 @@ async def get_fee(address: str, contract: AsyncContract) -> float: """ pass + @abstractmethod + def get_pool_func_call(self, addr1, addr2, *args, **kwargs): + ... + @staticmethod @abstractmethod async def get_tkn0(address: str, contract: AsyncContract, event: Any) -> str: diff --git a/fastlane_bot/events/exchanges/carbon_v1.py b/fastlane_bot/events/exchanges/carbon_v1.py index df0540661..fa4a40107 100644 --- a/fastlane_bot/events/exchanges/carbon_v1.py +++ b/fastlane_bot/events/exchanges/carbon_v1.py @@ -244,3 +244,6 @@ def save_strategy( ), block_number=block_number, ) + + def get_pool_func_call(self, addr1, addr2): + raise NotImplementedError