diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b54da5c2..5557e0da9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -209,7 +209,7 @@ Merged pull requests - Add Support for multiple Carbon deployments per-chain [\#379](https://github.com/bancorprotocol/fastlane-bot/issues/379) - Add support for Fantom Network [\#369](https://github.com/bancorprotocol/fastlane-bot/issues/369) -- python main.py --arb\_mode=b3\_two\_hop --alchemy\_max\_block\_fetch=200 --loglevel=INFO --backdate\_pools=False --polling\_interval=0 --reorg\_delay=0 --run\_data\_validator=False --limit\_bancor3\_flashloan\_tokens=True --randomizer=2 --default\_min\_profit\_gas\_token=0.01 --exchanges=carbon\_v1,bancor\_v3,uniswap\_v3,uniswap\_v2,sushiswap\_v2,balancer,pancakeswap\_v2,pancakeswap\_v3 --flashloan\_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,0x514910771AF9Ca656af840dff83E8264EcF986CA" [\#367](https://github.com/bancorprotocol/fastlane-bot/issues/367) +- python main.py --arb\_mode=b3\_two\_hop --alchemy\_max\_block\_fetch=200 --loglevel=INFO --backdate\_pools=False --polling\_interval=0 --reorg\_delay=0 --run\_data\_validator=False --limit\_bancor3\_flashloan\_tokens=True --default\_min\_profit\_gas\_token=0.01 --exchanges=carbon\_v1,bancor\_v3,uniswap\_v3,uniswap\_v2,sushiswap\_v2,balancer,pancakeswap\_v2,pancakeswap\_v3 --flashloan\_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,0x514910771AF9Ca656af840dff83E8264EcF986CA" [\#367](https://github.com/bancorprotocol/fastlane-bot/issues/367) - Minor optimization in multicaller [\#348](https://github.com/bancorprotocol/fastlane-bot/issues/348) - Add Handling of Flashloan Fee [\#346](https://github.com/bancorprotocol/fastlane-bot/issues/346) - support fusionx v3 on mantle [\#491](https://github.com/bancorprotocol/fastlane-bot/pull/491) ([NIXBNT](https://github.com/NIXBNT)) diff --git a/README.md b/README.md index 92a153021..248236e0d 100644 --- a/README.md +++ b/README.md @@ -108,13 +108,10 @@ You can configure the Fastlane Arbitrage Bot using the options in the `@click.op - **Triangular**: This includes arbitrage trades between three liquidity pools that can create a triangular route, starting and ending in the same token. For example, USDC > ETH, ETH > LINK, LINK > USDC - **Multi**: These modes can trade through multiple Carbon orders as a single trade. - **arb_mode options**: - - **single**: Pairwise arbitrage between one Carbon curve and one other exchange curve. - - **multi** Pairwise arbitrage between **multiple** Carbon curves and one other exchange curve. - - **triangle**: Triangular arbitrage between one Carbon curve and two other exchange curves. - - **multi_triangle**: Triangular arbitrage between **multiple** Carbon curves and two other exchange curves. + - **multi_triangle**: Triangular arbitrage between multiple Carbon curves and two other exchange curves. + - **multi_triangle_complete**: Triangular arbitrage between multiple Carbon curves and two other exchange curves (experimental). - **b3_two_hop**: Triangular arbitrage - the same as bancor_v3 mode but more gas-efficient. - **multi_pairwise_pol**: Pairwise multi-mode that always routes through the Bancor protocol-owned liquidity contract. - - **multi_pairwise_bal**: Pairwise multi-mode that always routes through Balancer. - **multi_pairwise_all**: **(Default)** Pairwise multi-mode that searches all available exchanges for pairwise arbitrage. - **flashloan_tokens** (str): Tokens the bot can use for flash loans. Specify token addresses as a comma-separated string (e.g., 0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2). - **n_jobs** (int): The number of parallel jobs to run. The default, -1, will use all available cores for the process. @@ -125,8 +122,6 @@ You can configure the Fastlane Arbitrage Bot using the options in the `@click.op - **logging_path** (str): The path for log files. **Recommended not to modify.** - **loglevel** (str): Logging level, which can be DEBUG, INFO, WARNING, or ERROR. - **use_cached_events** (bool): **Testing option.** This option runs the bot using historical cached events. -- **run_data_validator** (bool): This option validates that pool data hasn't changed from the time an opportunity was found. This can be useful if the bot has slow cycles, for example if an arb mode takes a long time to run. -- **randomizer** (int): The bot will randomly select an opportunity from the number of opportunities specified in this configuration after sorting by profit. For example the default setting 3 means the bot will randomly pick one of the 3 most profitable opportunities it found in the randomizer. - **limit_bancor3_flashloan_tokens** (bool): If True, this limits the flashloan tokens to tokens supported by Bancor V3. - **default_min_profit_gas_token** (float): The minimum amount of expected profit, denominated in the gas token, to consider executing an arbitrage trade. - **timeout** (int): **Testing option.** This will stop the bot after the specified amount of time has passed. @@ -146,7 +141,7 @@ You can configure the Fastlane Arbitrage Bot using the options in the `@click.op Specify options in the command line. For example: ```bash -poetry run python main.py --arb_mode=multi --polling_interval=12 --reorg_delay=10 --loglevel=INFO +poetry run python main.py --arb_mode=multi_pairwise_all --polling_interval=12 --reorg_delay=10 --loglevel=INFO ``` ## Troubleshooting @@ -172,18 +167,18 @@ The following examples are command-line inputs that start the bot with different #### Bancor V3-focused arbitrage ```commandline -poetry run python main.py --arb_mode=b3_two_hop --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --run_data_validator=False --limit_bancor3_flashloan_tokens=True --randomizer=2 --default_min_profit_gas_token=0.01 --exchanges=carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,0x514910771AF9Ca656af840dff83E8264EcF986CA" +poetry run python main.py --arb_mode=b3_two_hop --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --limit_bancor3_flashloan_tokens=True --default_min_profit_gas_token=0.01 --exchanges=carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,0x514910771AF9Ca656af840dff83E8264EcF986CA" ``` #### Carbon-focused pairwise arbitrage ```commandline -poetry run python main.py --arb_mode=multi --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --run_data_validator=False --default_min_profit_gas_token=0.01 --randomizer=2 --exchanges=bancor_v3,bancor_v2,carbon_v1,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" +poetry run python main.py --arb_mode=multi_pairwise_all --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --default_min_profit_gas_token=0.01 --exchanges=bancor_v3,bancor_v2,carbon_v1,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" ``` #### Unfocused pairwise arbitrage ```commandline -poetry run python main.py --arb_mode=multi_pairwise_all --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --run_data_validator=False --default_min_profit_gas_token=0.01 --randomizer=2 --exchanges=bancor_v3,bancor_v2,carbon_v1,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" +poetry run python main.py --arb_mode=multi_pairwise_all --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --default_min_profit_gas_token=0.01 --exchanges=bancor_v3,bancor_v2,carbon_v1,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" ``` #### Triangular Carbon-focused arbitrage ```commandline -poetry run python main.py --arb_mode=multi_triangle --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --run_data_validator=False --default_min_profit_gas_token=0.01 --randomizer=2 --exchanges=bancor_v3,bancor_v2,carbon_v1,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" +poetry run python main.py --arb_mode=multi_triangle --alchemy_max_block_fetch=200 --loglevel=INFO --backdate_pools=False --polling_interval=0 --reorg_delay=0 --default_min_profit_gas_token=0.01 --exchanges=bancor_v3,bancor_v2,carbon_v1,uniswap_v3,uniswap_v2,sushiswap_v2,balancer,pancakeswap_v2,pancakeswap_v3 --flashloan_tokens="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" ``` \ No newline at end of file diff --git a/fastlane_bot/bot.py b/fastlane_bot/bot.py index 5fe5800d5..a21b463b5 100644 --- a/fastlane_bot/bot.py +++ b/fastlane_bot/bot.py @@ -46,14 +46,12 @@ __VERSION__ = "3-b2.2" __DATE__ = "20/June/2023" -import random import json import os from _decimal import Decimal from dataclasses import dataclass, asdict, field from datetime import datetime -from typing import Generator, List, Dict, Tuple, Any, Callable -from typing import Optional +from typing import Any, List, Dict, Tuple, Optional from fastlane_bot.config import Config from fastlane_bot.helpers import ( @@ -66,17 +64,15 @@ split_carbon_trades, maximize_last_trade_per_tkn ) -from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer, T +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, CPCContainer from .config.constants import FLASHLOAN_FEE_MAP from .events.interface import QueryInterface -from .modes.pairwise_multi import FindArbitrageMultiPairwise -from .modes.pairwise_multi_all import FindArbitrageMultiPairwiseAll -from .modes.pairwise_multi_pol import FindArbitrageMultiPairwisePol -from .modes.pairwise_single import FindArbitrageSinglePairwise +from .modes.base import ArbitrageFinderBase +from .modes.pairwise_multi_all import ArbitrageFinderMultiPairwiseAll +from .modes.pairwise_multi_pol import ArbitrageFinderMultiPairwisePol from .modes.triangle_multi import ArbitrageFinderTriangleMulti -from .modes.triangle_single import ArbitrageFinderTriangleSingle +from .modes.triangle_multi_complete import ArbitrageFinderTriangleMultiComplete from .modes.triangle_bancor_v3_two_hop import ArbitrageFinderTriangleBancor3TwoHop -from .utils import num_format @dataclass @@ -101,19 +97,6 @@ class CarbonBot: SCALING_FACTOR = 0.999 - ARB_FINDER = { - "single": FindArbitrageSinglePairwise, - "multi": FindArbitrageMultiPairwise, - "triangle": ArbitrageFinderTriangleSingle, - "multi_triangle": ArbitrageFinderTriangleMulti, - "b3_two_hop": ArbitrageFinderTriangleBancor3TwoHop, - "multi_pairwise_pol": FindArbitrageMultiPairwisePol, - "multi_pairwise_all": FindArbitrageMultiPairwiseAll, - } - - class NoArbAvailable(Exception): - pass - def __post_init__(self): """ The post init method. @@ -192,8 +175,6 @@ def _simple_ordering_by_src_token( """ Reorders a trade_instructions_dct so that all items where the best_src_token is the tknin are before others """ - if best_trade_instructions_dic is None: - raise self.NoArbAvailable(f"No arb available for token {best_src_token}") src_token_instr = [ x for x in best_trade_instructions_dic if x["tknin"] == best_src_token ] @@ -209,8 +190,7 @@ def _simple_ordering_by_src_token( src_token_instr + non_src_token_instr + src_token_end ) - tx_in_count = len(src_token_instr) - return ordered_trade_instructions_dct, tx_in_count + return ordered_trade_instructions_dct def _basic_scaling(self, best_trade_instructions_dic, best_src_token): """ @@ -262,46 +242,26 @@ def _get_deadline(self, block_number) -> int: int The deadline (as UNIX epoch). """ - block_number = ( - self.ConfigObj.w3.eth.block_number if block_number is None else block_number - ) - return ( - self.ConfigObj.w3.eth.get_block(block_number).timestamp - + self.ConfigObj.DEFAULT_BLOCKTIME_DEVIATION - ) - - @classmethod - def _get_arb_finder(cls, arb_mode: str) -> Callable: - return cls.ARB_FINDER[arb_mode] + if block_number is None: + block_number = self.ConfigObj.w3.eth.block_number + return self.ConfigObj.w3.eth.get_block(block_number).timestamp + self.ConfigObj.DEFAULT_BLOCKTIME_DEVIATION - def _find_arbitrage( - self, - flashloan_tokens: List[str], - CCm: CPCContainer, - arb_mode: str, - randomizer: int - ) -> dict: - 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": finder, "r": finder.find_arbitrage()} + def get_arb_finder(self, arb_mode: str, flashloan_tokens, CCm) -> ArbitrageFinderBase: + return { + "multi_triangle": ArbitrageFinderTriangleMulti, + "b3_two_hop": ArbitrageFinderTriangleBancor3TwoHop, + "multi_pairwise_pol": ArbitrageFinderMultiPairwisePol, + "multi_pairwise_all": ArbitrageFinderMultiPairwiseAll, + "multi_triangle_complete": ArbitrageFinderTriangleMultiComplete, + }[arb_mode](flashloan_tokens=flashloan_tokens, CCm=CCm, ConfigObj=self.ConfigObj) - def _run( + def run( self, - flashloan_tokens: List[str], - CCm: CPCContainer, *, - arb_mode: str, - randomizer: int, - data_validator=False, + flashloan_tokens: List[str] = None, + CCm: CPCContainer = None, + arb_mode: str = None, logging_path: str = None, - replay_mode: bool = False, replay_from_block: int = None, ): """ @@ -310,283 +270,58 @@ def _run( Parameters ---------- flashloan_tokens: List[str] - The tokens to flashloan. + The flashloan tokens CCm: CPCContainer - The container. + The complete market data container arb_mode: str - The arbitrage mode. - randomizer: int - 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 + The arbitrage mode logging_path: str - the logging path (default: None) - replay_mode: bool - whether to run in replay mode (default: False) + The logging path 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) - finder, r = [arbitrage[key] for key in ["finder", "r"]] - - if r is None or len(r) == 0: - self.ConfigObj.logger.info("[bot._run] No eligible arb opportunities.") - return - - self.ConfigObj.logger.info( - f"[bot._run] Found {len(r)} eligible arb opportunities." - ) - r = self.randomize(arb_opps=r, randomizer=randomizer) - - if data_validator: - r = self.validate_optimizer_trades(arb_opp=r, arb_finder=finder) - if r is None: - self.ConfigObj.logger.warning( - "[bot._run] Math validation eliminated arb opportunity, restarting." - ) - return - if replay_mode: - pass - elif self.validate_pool_data(arb_opp=r): - self.ConfigObj.logger.debug( - "[bot._run] All data checks passed! Pools in sync!" - ) - else: - self.ConfigObj.logger.warning( - "[bot._run] Data validation failed. Updating pools and restarting." - ) - return - - tx_hash, tx_receipt = self._handle_trade_instructions(CCm, arb_mode, r, replay_from_block) - - if tx_hash: - tx_status = ["failed", "succeeded"][tx_receipt["status"]] if tx_receipt else "pending" - tx_details = json.dumps(tx_receipt, indent=4) if tx_receipt else "no receipt" - self.ConfigObj.logger.info(f"Arbitrage transaction {tx_hash} {tx_status}") - - if logging_path: - filename = f"tx_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt" - 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): - """ - Validates arbitrage trade input using equations that account for fees. - This has limited coverage, but is very effective for the instances it covers. - - Parameters - ---------- - arb_opp: tuple - The tuple containing an arbitrage opportunity found by the Optimizer - arb_finder: Any - The Arb mode class that handles the differences required for each arb route. - - Returns - ------- - tuple or None + The block number to start replaying from """ - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb_opp - - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = self._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token - ) - cids = [] - for pool in ordered_trade_instructions_dct: - pool_cid = pool["cid"] - if "-0" in pool_cid or "-1" in pool_cid: - self.ConfigObj.logger.debug( - f"[bot.validate_optimizer_trades] Math arb validation not currently supported for arbs with " - f"Carbon, returning to main flow." - ) - return arb_opp - cids.append(pool_cid) - if len(cids) > 3: - self.ConfigObj.logger.warning( - f"[bot.validate_optimizer_trades] Math validation not supported for more than 3 pools, returning " - f"to main flow." - ) - return arb_opp - max_trade_in = arb_finder.get_optimal_arb_trade_amts( - cids=cids, flt=best_src_token - ) - if max_trade_in is None: - return None - if type(max_trade_in) != float and type(max_trade_in) != int: - return None - if max_trade_in < 0.0: - return None - self.ConfigObj.logger.debug( - f"[bot.validate_optimizer_trades] max_trade_in equation = {max_trade_in}, optimizer trade in = {ordered_trade_instructions_dct[0]['amtin']}" - ) - ordered_trade_instructions_dct[0]["amtin"] = max_trade_in - - best_trade_instructions_dic = ordered_trade_instructions_dct - - return ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) - - def validate_pool_data(self, arb_opp): - """ - Validates that the data for each pool in the arbitrage opportunity is fresh. - - Parameters - ---------- - arb_opp: tuple - The tuple containing an arbitrage opportunity found by the Optimizer - - Returns - ------- - bool - """ - self.ConfigObj.logger.info("[bot.validate_pool_data] Validating pool data...") - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb_opp - for pool in best_trade_instructions_dic: - pool_cid = pool["cid"].split("-")[0] - strategy_id = pool["strategy_id"] - current_pool = self.db.get_pool(cid=pool_cid) - pool_info = { - "cid": pool_cid, - "strategy_id": strategy_id, - "id": current_pool.id, - "address": current_pool.address, - "pair_name": current_pool.pair_name, - "exchange_name": current_pool.exchange_name, - "tkn0_address": current_pool.tkn0_address, - "tkn1_address": current_pool.tkn1_address, - "tkn0_symbol": current_pool.tkn0_symbol, - "tkn1_symbol": current_pool.tkn1_symbol, - "tkn0_decimals" : current_pool.tkn0_decimals, - "tkn1_decimals": current_pool.tkn1_decimals, - } - - self.db.mgr.update_from_pool_info(pool_info=pool_info) - self.ConfigObj.logger.debug(f"[bot.validate_pool_data] pool_cid: {pool_cid}") - self.ConfigObj.logger.debug(f"[bot.validate_pool_data] pool_info: {pool_info}") - - if current_pool.exchange_name in self.ConfigObj.CARBON_V1_FORKS: - if ( - current_pool.y_0 != pool_info["y_0"] - or current_pool.y_1 != pool_info["y_1"] - ): - self.ConfigObj.logger.debug( - "[bot.validate_pool_data] Carbon pool not up to date, updating and restarting." - ) - return False - elif current_pool.exchange_name in [ - "balancer", - ]: - for idx, balance in enumerate(current_pool.token_balances): - if balance != pool_info[f"tkn{idx}_balance"]: - self.ConfigObj.logger.debug( - "[bot.validate_pool_data] Balancer pool not up to date, updating and restarting." - ) - return False - elif current_pool.exchange_name in self.ConfigObj.UNI_V3_FORKS: - if ( - current_pool.liquidity != pool_info["liquidity"] - or current_pool.sqrt_price_q96 != pool_info["sqrt_price_q96"] - or current_pool.tick != pool_info["tick"] - ): - self.ConfigObj.logger.debug( - "[bot.validate_pool_data] UniV3 pool not up to date, updating and restarting." - ) - return False - - elif ( - current_pool.tkn0_balance != pool_info["tkn0_balance"] - or current_pool.tkn1_balance != pool_info["tkn1_balance"] - ): - self.ConfigObj.logger.debug( - f"[bot.validate_pool_data] {pool_info['exchange_name']} pool not up to date, updating and restarting." - ) - return False - - return True + if flashloan_tokens is None: + flashloan_tokens = self.RUN_FLASHLOAN_TOKENS + if CCm is None: + CCm = self.get_curves() - @staticmethod - def randomize(arb_opps, randomizer: int = 1): - """ - Sorts arb opportunities by profit, then returns a random element from the top N arbs, with N being the value input in randomizer. - :param arb_opps: Arb opportunities - :param randomizer: the number of arb ops to choose from after sorting by profit. For example, a value of 3 would be the top 3 arbs by profitability. - returns: - A randomly selected arb opportunity. + arb_finder = self.get_arb_finder(arb_mode, flashloan_tokens, CCm) + arb_opps = arb_finder.find_arb_opps() - """ - 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) + if len(arb_opps) == 0: + self.ConfigObj.logger.info("[bot.run] No eligible arb opportunities.") + return - @staticmethod - def _carbon_in_trade_route(trade_instructions: List[TradeInstruction]) -> bool: - """ - Returns True if the exchange route includes Carbon - """ - return any(trade.is_carbon for trade in trade_instructions) - - def get_prices_simple(self, CCm, tkn0, tkn1): - curve_prices = [(x.params['exchange'],x.descr,x.cid,x.p) for x in CCm.bytknx(tkn0).bytkny(tkn1)] - curve_prices += [(x.params['exchange'],x.descr,x.cid,1/x.p) for x in CCm.bytknx(tkn1).bytkny(tkn0)] - return curve_prices - - # Global constant for Carbon Forks ordering - CARBON_SORTING_ORDER = float('inf') - - # Create a sort order mapping function - def create_sort_order(self, sort_sequence): - # Create a dictionary mapping from sort sequence to indices, except for Carbon Forks - return {key: index for index, key in enumerate(sort_sequence) if key not in self.ConfigObj.CARBON_V1_FORKS} - - # Define the sort key function separately - def sort_key(self, item, sort_order): - # Check if the item is Carbon Forks - if item[0] in self.ConfigObj.CARBON_V1_FORKS: - return self.CARBON_SORTING_ORDER - # Otherwise, use the sort order from the dictionary, or a default high value - return sort_order.get(item[0], self.CARBON_SORTING_ORDER - 1) - - # Define the custom sort function - def custom_sort(self, data, sort_sequence): - sort_order = self.create_sort_order(sort_sequence) - return sorted(data, key=lambda item: self.sort_key(item, sort_order)) + self.ConfigObj.logger.info(f"[bot.run] Found {len(arb_opps)} eligible arb opportunities.") + + for arb_opp in arb_opps: + is_profitable, tx_hash, tx_receipt = self._handle_trade_instructions(arb_finder, arb_opp, replay_from_block) + if not is_profitable: + break + if tx_hash: + tx_status = ["failed", "succeeded"][tx_receipt["status"]] if tx_receipt else "pending" + tx_details = json.dumps(tx_receipt, indent=4) if tx_receipt else "no receipt" + self.ConfigObj.logger.info(f"Arbitrage transaction {tx_hash} {tx_status}") + if logging_path: + filename = f"tx_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt" + with open(os.path.join(logging_path, filename), "w") as f: + f.write(f"{tx_hash} {tx_status}: {tx_details}") def calculate_profit( self, - CCm: CPCContainer, + arb_finder: ArbitrageFinderBase, best_profit: Decimal, fl_token: str, flashloan_fee_amt: int = 0, - ) -> Tuple[Decimal, Decimal, Decimal]: + ) -> Tuple[Decimal, Decimal]: """ - Calculate the actual profit in USD. + Calculate the best profit in GAS token and in USD. Parameters ---------- - CCm: CPCContainer - The container. + arb_finder: ArbitrageFinderBase + The arbitrage finder. best_profit: Decimal The best profit. fl_token: str @@ -596,111 +331,29 @@ def calculate_profit( Returns ------- - Tuple[Decimal, Decimal, Decimal] - The updated best_profit, flt_per_bnt, and profit_usd. + Tuple[Decimal, Decimal] + best_profit_gastkn, best_profit_usd. """ - 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 - flashloan_fee_amt_fl_token = Decimal(str(flashloan_fee_amt)) - if fl_token not in [self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS]: - price_curves = self.get_prices_simple(CCm, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, fl_token) - sorted_price_curves = self.custom_sort(price_curves, sort_sequence) - self.ConfigObj.logger.debug(f"[bot.calculate_profit sort_sequence] {sort_sequence}") - self.ConfigObj.logger.debug(f"[bot.calculate_profit price_curves] {price_curves}") - self.ConfigObj.logger.debug(f"[bot.calculate_profit sorted_price_curves] {sorted_price_curves}") - if len(sorted_price_curves)>0: - fltkn_gastkn_conversion_rate = sorted_price_curves[0][-1] - flashloan_fee_amt_gastkn = Decimal(str(flashloan_fee_amt_fl_token)) / Decimal(str(fltkn_gastkn_conversion_rate)) - best_profit_gastkn = Decimal(str(best_profit_fl_token)) / Decimal(str(fltkn_gastkn_conversion_rate)) - flashloan_fee_amt_gastkn - self.ConfigObj.logger.debug(f"[bot.calculate_profit] {fl_token, best_profit_fl_token, fltkn_gastkn_conversion_rate, best_profit_gastkn, 'GASTOKEN'}") - else: - self.ConfigObj.logger.error( - f"[bot.calculate_profit] Failed to get conversion rate for {fl_token} and {self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS}. Raise" - ) - raise - else: - best_profit_gastkn = best_profit_fl_token - flashloan_fee_amt_fl_token + src_profit = Decimal(str(best_profit)) - Decimal(str(flashloan_fee_amt)) + best_profit_gastkn = arb_finder.calculate_profit(fl_token, src_profit) try: - price_curves_usd = self.get_prices_simple(CCm, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, self.ConfigObj.STABLECOIN_ADDRESS) - sorted_price_curves_usd = self.custom_sort(price_curves_usd, sort_sequence) - self.ConfigObj.logger.debug(f"[bot.calculate_profit price_curves_usd] {price_curves_usd}") - self.ConfigObj.logger.debug(f"[bot.calculate_profit sorted_price_curves_usd] {sorted_price_curves_usd}") - usd_gastkn_conversion_rate = Decimal(str(sorted_price_curves_usd[0][-1])) - except Exception: - usd_gastkn_conversion_rate = Decimal("NaN") - - best_profit_usd = best_profit_gastkn * usd_gastkn_conversion_rate - self.ConfigObj.logger.debug(f"[bot.calculate_profit] {'GASTOKEN', best_profit_gastkn, usd_gastkn_conversion_rate, best_profit_usd, 'USD'}") - return best_profit_fl_token, best_profit_gastkn, best_profit_usd - - @staticmethod - def calculate_arb( - arb_mode: str, - best_profit_gastkn: Decimal, - best_profit_usd: Decimal, - flashloan_tkn_profit: Decimal, - calculated_trade_instructions: List[Any], - fl_token: str, - ) -> dict: - """ - Calculate the arbitrage. - - Parameters - ---------- - arb_mode: str - The arbitrage mode. - best_profit: Decimal - The best profit. - best_profit_usd: Decimal - The profit in USD. - flashloan_tkn_profit: Decimal - The profit from flashloan token. - calculated_trade_instructions: List[Any] - The calculated trade instructions. - fl_token: str - The flashloan token. + best_profit_usd = 1 / arb_finder.calculate_profit(self.ConfigObj.STABLECOIN_ADDRESS, 1 / best_profit_gastkn) + except Exception as e: + best_profit_usd = Decimal("NaN") + self.ConfigObj.logger.info(f"[bot.calculate_profit] error: {e}") - Returns - ------- - dict - The arbitrage. - """ + self.ConfigObj.logger.debug(f"[bot.calculate_profit] input: {best_profit, fl_token, flashloan_fee_amt}") + self.ConfigObj.logger.debug(f"[bot.calculate_profit] output: {best_profit_gastkn, best_profit_usd}") - return { - "type": arb_mode, - "profit_gas_token": num_format(best_profit_gastkn), - "profit_usd": num_format(best_profit_usd), - "flashloan": [ - { - "token": fl_token, - "amount": num_format(calculated_trade_instructions[0].amtin), - "profit": num_format(flashloan_tkn_profit) - } - ], - "trades": [ - { - "trade_index": idx, - "exchange": trade.exchange_name, - "tkn_in": {trade.tknin_symbol: trade.tknin} if trade.tknin_symbol != trade.tknin else trade.tknin, - "amount_in": num_format(trade.amtin), - "tkn_out": {trade.tknout_symbol: trade.tknout} if trade.tknout_symbol != trade.tknout else trade.tknout, - "amt_out": num_format(trade.amtout), - "cid0": trade.cid[-10:] - } - for idx, trade in enumerate(calculated_trade_instructions) - ] - } + return best_profit_gastkn, best_profit_usd def _handle_trade_instructions( self, - CCm: CPCContainer, - arb_mode: str, - r: Any, + arb_finder: ArbitrageFinderBase, + arb_opp: dict, replay_from_block: int = None - ) -> Tuple[Optional[str], Optional[dict]]: + ) -> Tuple[bool, Optional[str], Optional[dict]]: """ Creates and executes the trade instructions @@ -709,39 +362,30 @@ def _handle_trade_instructions( Parameters ---------- - CCm: CPCContainer - The container. - arb_mode: str - The arbitrage mode. - r: Any - The result. + arb_finder: ArbitrageFinderBase + The arbitrage finder. + arb_opp: dictionary + The dictionary containing an arbitrage opportunity found by the Optimizer replay_from_block: int the block number to start replaying from (default: None) Returns ------- + - True if the profit is sufficiently high, False otherwise. - The hash of the transaction if submitted, None otherwise. - The receipt of the transaction if completed, None otherwise. """ - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = r + src_token = arb_opp["src_token"] + trade_instructions_dic = arb_opp["trade_instructions_dic"] # Order the trade instructions - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = self._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token + ordered_trade_instructions_dct = self._simple_ordering_by_src_token( + trade_instructions_dic, src_token ) # Scale the trade instructions ordered_scaled_dcts = self._basic_scaling( - ordered_trade_instructions_dct, best_src_token + ordered_trade_instructions_dct, src_token ) # Convert the trade instructions @@ -755,83 +399,88 @@ def _handle_trade_instructions( ) # Aggregate the carbon trades - agg_trade_instructions = ( + trade_instructions = ( tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) - if self._carbon_in_trade_route(ordered_trade_instructions_objects) + if any(trade.is_carbon for trade in ordered_trade_instructions_objects) else ordered_trade_instructions_objects ) # Calculate the trade instructions - calculated_trade_instructions = tx_route_handler.calculate_trade_outputs( - agg_trade_instructions + tx_route_handler.calculate_trade_outputs( + trade_instructions ) # Aggregate multiple Bancor V3 trades into a single trade - calculated_trade_instructions = tx_route_handler.aggregate_bancor_v3_trades( - calculated_trade_instructions + tx_route_handler.aggregate_bancor_v3_trades( + trade_instructions ) flashloan_struct = tx_route_handler.generate_flashloan_struct( - trade_instructions_objects=calculated_trade_instructions + trade_instructions_objects=trade_instructions ) # Get the flashloan token - fl_token = calculated_trade_instructions[0].tknin_address - fl_token_symbol = calculated_trade_instructions[0].tknin_symbol - fl_token_decimals = calculated_trade_instructions[0].tknin_decimals - flashloan_amount_wei = int(calculated_trade_instructions[0].amtin_wei) + fl_token = trade_instructions[0].tknin_address + fl_token_symbol = trade_instructions[0].tknin_symbol + fl_token_decimals = trade_instructions[0].tknin_decimals + flashloan_amount_wei = int(trade_instructions[0].amtin_wei) flashloan_fee = FLASHLOAN_FEE_MAP.get(self.ConfigObj.NETWORK, 0) flashloan_fee_amt = flashloan_fee * (flashloan_amount_wei / 10**int(fl_token_decimals)) - best_profit = flashloan_tkn_profit = tx_route_handler.calculate_trade_profit( - calculated_trade_instructions + flashloan_tkn_profit = tx_route_handler.calculate_trade_profit( + trade_instructions ) # Calculate the best profit - best_profit_fl_token, best_profit_gastkn, best_profit_usd = self.calculate_profit( - CCm, best_profit, fl_token, flashloan_fee_amt - ) - - # Log the best profit - self.ConfigObj.logger.debug( - f"[bot._handle_trade_instructions] Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}" - ) - - # Calculate the arbitrage - arb = self.calculate_arb( - arb_mode, - best_profit_gastkn, - best_profit_usd, - flashloan_tkn_profit, - calculated_trade_instructions, - fl_token_symbol, - ) - - # Log the arbitrage - self.ConfigObj.logger.info( - f"[bot._handle_trade_instructions] calculated arb: {arb}" + best_profit_gastkn, best_profit_usd = self.calculate_profit( + arb_finder, flashloan_tkn_profit, fl_token, flashloan_fee_amt ) # Check if the best profit is greater than the minimum profit if best_profit_gastkn < self.ConfigObj.DEFAULT_MIN_PROFIT_GAS_TOKEN: self.ConfigObj.logger.info( - f"[bot._handle_trade_instructions] Opportunity with profit: {num_format(best_profit_gastkn)} does not meet minimum profit: {self.ConfigObj.DEFAULT_MIN_PROFIT_GAS_TOKEN}, discarding." + f"[bot._handle_trade_instructions]:\n" + f"- Expected profit: {best_profit_gastkn}\n" + f"- Minimum profit: {self.ConfigObj.DEFAULT_MIN_PROFIT_GAS_TOKEN}\n" ) - return None, None - - # Log the flashloan amount - self.ConfigObj.logger.debug( - f"[bot._handle_trade_instructions] Flashloan amount: {flashloan_amount_wei}" + return False, None, None + + # Log the arbitrage details + arb_info = [ + f"gas profit = {best_profit_gastkn}", + f"usd profit = {best_profit_usd}", + f"flashloan token = {fl_token_symbol}", + f"flashloan amount = {trade_instructions[0].amtin}", + f"flashloan profit = {flashloan_tkn_profit}" + ] + arb_ti_info = [ + { + "exchange": trade.exchange_name, + "tkn_in": {trade.tknin_symbol: trade.tknin} if trade.tknin_symbol != trade.tknin else trade.tknin, + "amt_in": trade.amtin, + "tkn_out": {trade.tknout_symbol: trade.tknout} if trade.tknout_symbol != trade.tknout else trade.tknout, + "amt_out": trade.amtout + } + for trade in trade_instructions + ] + arb_details = "\n".join( + [ + "arbitrage details:", + *[f"- {line}" for line in arb_info], + "- trade instructions:", + *[f" {index + 1}. {line}" for index, line in enumerate(arb_ti_info)] + ] ) + self.ConfigObj.logger.info(f"[bot._handle_trade_instructions] {arb_details}") # Split Carbon Orders split_calculated_trade_instructions = split_carbon_trades( cfg=self.ConfigObj, - trade_instructions=calculated_trade_instructions + trade_instructions=trade_instructions ) # Encode the trade instructions - encoded_trade_instructions = tx_route_handler.custom_data_encoder( + tx_route_handler.custom_data_encoder( split_calculated_trade_instructions ) @@ -842,7 +491,7 @@ def _handle_trade_instructions( route_struct = [ asdict(rs) for rs in tx_route_handler.get_route_structs( - trade_instructions=encoded_trade_instructions, deadline=deadline + trade_instructions=split_calculated_trade_instructions, deadline=deadline ) ] @@ -866,79 +515,23 @@ def _handle_trade_instructions( 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}" + f"[bot._handle_trade_instructions] Trade Instructions: \n {trade_instructions_dic}" ) # Validate and submit the transaction - return self.tx_helpers.validate_and_submit_transaction( - route_struct=route_struct_processed, - src_amt=flashloan_amount_wei, - src_address=fl_token, - expected_profit_gastkn=best_profit_gastkn, - expected_profit_usd=best_profit_usd, - flashloan_struct=flashloan_struct, - ) - - def get_tokens_in_exchange( - self, - exchange_name: str, - ) -> List[str]: - """ - Gets all tokens that exist in pools on the specified exchange. - :param exchange_name: the exchange name - """ - return self.db.get_tokens_from_exchange(exchange_name=exchange_name) - - def run( - self, - *, - flashloan_tokens: List[str] = None, - CCm: CPCContainer = None, - arb_mode: str = None, - run_data_validator: bool = False, - randomizer: int = 0, - logging_path: str = None, - replay_mode: bool = False, - replay_from_block: int = None, - ): - """ - Runs the bot. - - Parameters - ---------- - flashloan_tokens: List[str] - The flashloan tokens (optional; default: RUN_FLASHLOAN_TOKENS) - CCm: CPCContainer - The complete market data container (optional; default: database via get_curves()) - arb_mode: str - the arbitrage mode (default: None; can be set depending on arbmode) - run_data_validator: bool - whether to run the data validator (default: False) - randomizer: int - the randomizer (default: 0) - logging_path: str - the logging path (default: None) - replay_mode: bool - whether to run in replay mode (default: False) - replay_from_block: int - the block number to start replaying from (default: None) - """ - - if flashloan_tokens is None: - flashloan_tokens = self.RUN_FLASHLOAN_TOKENS - if CCm is None: - CCm = self.get_curves() - - try: - self._run( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - arb_mode=arb_mode, - data_validator=run_data_validator, - randomizer=randomizer, - logging_path=logging_path, - replay_mode=replay_mode, - replay_from_block=replay_from_block, - ) - except self.NoArbAvailable as e: - self.ConfigObj.logger.info(e) + while True: + try: + tx_hash, tx_receipt = self.tx_helpers.validate_and_submit_transaction( + route_struct=route_struct_processed, + src_amt=flashloan_amount_wei, + src_address=fl_token, + expected_profit_gastkn=best_profit_gastkn, + expected_profit_usd=best_profit_usd, + flashloan_struct=flashloan_struct, + ) + return True, tx_hash, tx_receipt + except Exception as e: + if "less than block base fee" in str(e): + self.ConfigObj.logger.error(f"{e}; retrying...") + else: + raise diff --git a/fastlane_bot/config/network.py b/fastlane_bot/config/network.py index e8715d5f6..aabff3f6a 100644 --- a/fastlane_bot/config/network.py +++ b/fastlane_bot/config/network.py @@ -188,6 +188,7 @@ class ConfigNetwork(ConfigBase): ETH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" MKR_ADDRESS = "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2" + DAI_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F" LINK_ADDRESS = "0x514910771AF9Ca656af840dff83E8264EcF986CA" WBTC_ADDRESS = "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" BNT_ADDRESS = "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" @@ -345,9 +346,7 @@ def __post_init__(self): df=self.network_df, fork_name=S.UNISWAP_V3 ) self.SOLIDLY_FEE_MAPPING = get_fee_map(df=self.network_df, fork_name=S.SOLIDLY_V2) - self.UNI_V2_FORKS = [key for key in self.UNI_V2_ROUTER_MAPPING.keys()] + [ - "uniswap_v2" - ] + self.UNI_V2_FORKS = [key for key in self.UNI_V2_ROUTER_MAPPING.keys()] self.UNI_V3_FORKS = [key for key in self.UNI_V3_ROUTER_MAPPING.keys()] self.SOLIDLY_V2_ROUTER_MAPPING = get_fork_map( @@ -456,11 +455,6 @@ class _ConfigNetworkMainnet(ConfigNetwork): # FACTORY, CONVERTER, AND CONTROLLER ADDRESSES ####################################################################################### BANCOR_V3_NETWORK_INFO_ADDRESS = "0x8E303D296851B320e6a697bAcB979d13c9D6E760" - BANCOR_V3_NETWORK_ADDRESS = "0xeEF417e1D5CC832e619ae18D2F140De2999dD4fB" - BANCOR_V3_NETWORK_SETTINGS = "0x83E1814ba31F7ea95D216204BB45FE75Ce09b14F" - BANCOR_V3_VAULT = "0x649765821D9f64198c905eC0B2B037a4a52Bc373" - BANCOR_V3_POOL_COLLECTOR_ADDRESS = "0xde1B3CcfC45e3F5bff7f43516F2Cd43364D883E4" - BANCOR_V2_CONVERTER_REGISTRY_ADDRESS = "0xC0205e203F423Bcd8B2a4d6f8C8A154b0Aa60F19" # FASTLANE_CONTRACT_ADDRESS = "0x51a6D03B156af044bda570CF35a919DB851Cebd1" FASTLANE_CONTRACT_ADDRESS = "0x41Eeba3355d7D6FF628B7982F3F9D055c39488cB" CARBON_CONTROLLER_ADDRESS = "0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1" @@ -845,11 +839,6 @@ class _ConfigNetworkTenderly(ConfigNetwork): # FACTORY, CONVERTER, AND CONTROLLER ADDRESSES ####################################################################################### BANCOR_V3_NETWORK_INFO_ADDRESS = "0x8E303D296851B320e6a697bAcB979d13c9D6E760" - BANCOR_V3_NETWORK_ADDRESS = "0xeEF417e1D5CC832e619ae18D2F140De2999dD4fB" - BANCOR_V3_NETWORK_SETTINGS = "0x83E1814ba31F7ea95D216204BB45FE75Ce09b14F" - BANCOR_V3_VAULT = "0x649765821D9f64198c905eC0B2B037a4a52Bc373" - BANCOR_V3_POOL_COLLECTOR_ADDRESS = "0xde1B3CcfC45e3F5bff7f43516F2Cd43364D883E4" - BANCOR_V2_CONVERTER_REGISTRY_ADDRESS = "0xC0205e203F423Bcd8B2a4d6f8C8A154b0Aa60F19" # FASTLANE_CONTRACT_ADDRESS = "0x51a6D03B156af044bda570CF35a919DB851Cebd1" FASTLANE_CONTRACT_ADDRESS = "0x41Eeba3355d7D6FF628B7982F3F9D055c39488cB" CARBON_CONTROLLER_ADDRESS = "0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1" diff --git a/fastlane_bot/config/provider.py b/fastlane_bot/config/provider.py index 49a3e1985..9da93d7a1 100644 --- a/fastlane_bot/config/provider.py +++ b/fastlane_bot/config/provider.py @@ -139,7 +139,7 @@ def __init__(self, network: ConfigNetwork, **kwargs): provider_url=self.RPC_URL, provider_name="tenderly", ) - self.connection.connect_network() + self.connection.connect_network(network.IS_INJECT_POA_MIDDLEWARE) self.w3 = self.connection.web3 self.BANCOR_ARBITRAGE_CONTRACT = self.w3.eth.contract( diff --git a/fastlane_bot/data/abi.py b/fastlane_bot/data/abi.py index 3c98f7000..ea3a0c028 100644 --- a/fastlane_bot/data/abi.py +++ b/fastlane_bot/data/abi.py @@ -597,38 +597,6 @@ } ] -BANCOR_V3_NETWORK_ABI = [ - { - "type": "event", - "name": "PoolCreated", - "anonymous": False, - "inputs": [{"indexed": True, "internalType": "contract Token", "name": "pool", "type": "address"}, {"indexed": True, "internalType": "contract IPoolCollection", "name": "poolCollection", "type": "address"}] - }, - { - "type": "event", - "name": "TokensTraded", - "anonymous": False, - "inputs": [{"indexed": True, "internalType": "bytes32", "name": "contextId", "type": "bytes32"}, {"indexed": True, "internalType": "contract Token", "name": "sourceToken", "type": "address"}, {"indexed": True, "internalType": "contract Token", "name": "targetToken", "type": "address"}, {"indexed": False, "internalType": "uint256", "name": "sourceAmount", "type": "uint256"}, {"indexed": False, "internalType": "uint256", "name": "targetAmount", "type": "uint256"}, {"indexed": False, "internalType": "uint256", "name": "bntAmount", "type": "uint256"}, {"indexed": False, "internalType": "uint256", "name": "targetFeeAmount", "type": "uint256"}, {"indexed": False, "internalType": "uint256", "name": "bntFeeAmount", "type": "uint256"}, {"indexed": False, "internalType": "address", "name": "trader", "type": "address"}] - }, - { - "type": "function", - "name": "withdrawPOL", - "stateMutability": "nonpayable", - "inputs": [{"internalType": "contract Token", "name": "pool", "type": "address"}], - "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}] - } -] - -BANCOR_V3_NETWORK_SETTINGS = [ - { - "type": "function", - "name": "tokenWhitelistForPOL", - "stateMutability": "view", - "inputs": [], - "outputs": [{"internalType": "contract Token[]", "name": "", "type": "address[]"}] - } -] - BANCOR_V3_NETWORK_INFO_ABI = [ { "type": "function", @@ -665,13 +633,6 @@ "anonymous": False, "inputs": [{"indexed": True, "internalType": "bytes32", "name": "contextId", "type": "bytes32"}, {"indexed": True, "internalType": "contract Token", "name": "pool", "type": "address"}, {"indexed": True, "internalType": "contract Token", "name": "tkn_address", "type": "address"}, {"indexed": False, "internalType": "uint256", "name": "prevLiquidity", "type": "uint256"}, {"indexed": False, "internalType": "uint256", "name": "newLiquidity", "type": "uint256"}] }, - { - "type": "function", - "name": "poolData", - "stateMutability": "view", - "inputs": [{"internalType": "contract Token", "name": "pool", "type": "address"}], - "outputs": [{"components": [{"internalType": "contract IPoolToken", "name": "poolToken", "type": "address"}, {"internalType": "uint32", "name": "tradingFeePPM", "type": "uint32"}, {"internalType": "bool", "name": "tradingEnabled", "type": "bool"}, {"internalType": "bool", "name": "depositingEnabled", "type": "bool"}, {"components": [{"internalType": "uint32", "name": "blockNumber", "type": "uint32"}, {"components": [{"internalType": "uint112", "name": "n", "type": "uint112"}, {"internalType": "uint112", "name": "d", "type": "uint112"}], "internalType": "struct Fraction112", "name": "rate", "type": "tuple"}, {"components": [{"internalType": "uint112", "name": "n", "type": "uint112"}, {"internalType": "uint112", "name": "d", "type": "uint112"}], "internalType": "struct Fraction112", "name": "invRate", "type": "tuple"}], "internalType": "struct AverageRates", "name": "averageRates", "type": "tuple"}, {"components": [{"internalType": "uint128", "name": "bntTradingLiquidity", "type": "uint128"}, {"internalType": "uint128", "name": "baseTokenTradingLiquidity", "type": "uint128"}, {"internalType": "uint256", "name": "stakedBalance", "type": "uint256"}], "internalType": "struct PoolLiquidity", "name": "liquidity", "type": "tuple"}], "internalType": "struct Pool", "name": "", "type": "tuple"}] - }, { "type": "function", "name": "tradingFeePPM", diff --git a/fastlane_bot/events/async_backdate_utils.py b/fastlane_bot/events/async_backdate_utils.py index cfe396b69..42dfa9ff9 100644 --- a/fastlane_bot/events/async_backdate_utils.py +++ b/fastlane_bot/events/async_backdate_utils.py @@ -13,10 +13,6 @@ from typing import Any, Dict, Tuple, List from web3 import AsyncWeb3 -from fastlane_bot.events.async_utils import ( - get_abis_and_exchanges, - get_contract_chunks, -) from fastlane_bot.events.utils import parse_non_multicall_rows_to_update @@ -30,17 +26,14 @@ async def async_main_backdate_from_contracts(c: List[Dict[str, Any]], w3_async: def async_backdate_from_contracts(mgr: Any, rows: List[int]): - abis = get_abis_and_exchanges(mgr) + abis = {exchange_name: exchange.get_abi() for exchange_name, exchange in mgr.exchanges.items()} contracts = get_backdate_contracts(abis, mgr, rows) - chunks = get_contract_chunks(contracts) + chunks = [contracts[i : i + 1000] for i in range(0, len(contracts), 1000)] for chunk in chunks: loop = asyncio.get_event_loop() vals = loop.run_until_complete(async_main_backdate_from_contracts(chunk, w3_async=mgr.w3_async)) - idxes = [val[0] for val in vals] - updated_pool_info = [val[1] for val in vals] - for i, idx in enumerate(idxes): - updated_pool_data = updated_pool_info[i] - mgr.pool_data[idx] = updated_pool_data + for val in vals: + mgr.pool_data[val[0]] = val[1] def get_backdate_contracts( @@ -57,8 +50,8 @@ def get_backdate_contracts( "tenderly_fork_id": mgr.tenderly_fork_id, "pool_info": pool_info, "contract": mgr.w3_async.eth.contract( - address=mgr.pool_data[idx]["address"], - abi=abis[mgr.pool_data[idx]["exchange_name"]], + address=pool_info["address"], + abi=abis[pool_info["exchange_name"]], ), } ) diff --git a/fastlane_bot/events/async_event_update_utils.py b/fastlane_bot/events/async_event_update_utils.py index a847599d0..4a21a8016 100644 --- a/fastlane_bot/events/async_event_update_utils.py +++ b/fastlane_bot/events/async_event_update_utils.py @@ -24,7 +24,6 @@ from fastlane_bot.config.constants import CARBON_V1_NAME from fastlane_bot.data.abi import ERC20_ABI -from fastlane_bot.events.async_utils import get_contract_chunks from fastlane_bot.events.utils import update_pools_from_events from fastlane_bot.events.pools.utils import get_pool_cid from .interfaces.event import Event @@ -52,28 +51,7 @@ async def _get_missing_tkn(mgr: Any, contract: AsyncContract, tkn: str) -> pd.Da decimals = await contract.functions.decimals().call() except Exception: decimals = None - try: - df = pd.DataFrame( - [ - { - "address": tkn, - "symbol": symbol, - "decimals": decimals, - } - ] - ) - except Exception as e: - mgr.cfg.logger.error(f"Failed to get token info for {tkn} {e}") - df = pd.DataFrame( - [ - { - "address": tkn, - "symbol": None, - "decimals": decimals, - } - ] - ) - return df + return pd.DataFrame([{"address": tkn, "symbol": symbol, "decimals": decimals}]) async def _get_missing_tkns(mgr: Any, c: List[Dict[str, Any]]) -> pd.DataFrame: @@ -316,7 +294,7 @@ def _process_contract_chunks( def _get_pool_contracts(mgr: Any) -> List[Dict[str, Any]]: contracts = [] - for add, en, event, key, value in mgr.pools_to_add_from_contracts: + for event in mgr.pools_to_add_from_contracts: exchange_name = mgr.exchange_name_from_event(event) ex = mgr.exchanges[exchange_name] abi = ex.get_abi() @@ -345,14 +323,9 @@ def async_update_pools_from_contracts(mgr: Any, current_block: int): orig_num_pools_in_data = len(mgr.pool_data) mgr.cfg.logger.info("Async process now updating pools from contracts...") - all_events = [ - event - for address, exchange_name, event, key, value in mgr.pools_to_add_from_contracts - ] - # split contracts into chunks of 1000 contracts = _get_pool_contracts(mgr) - chunks = get_contract_chunks(contracts) + chunks = [contracts[i : i + 1000] for i in range(0, len(contracts), 1000)] tokens_and_fee_df = _process_contract_chunks( mgr=mgr, base_filename="tokens_and_fee_df_", @@ -364,10 +337,11 @@ def async_update_pools_from_contracts(mgr: Any, current_block: int): ) contracts, tokens_df = _get_token_contracts(mgr, tokens_and_fee_df) + chunks = [contracts[i : i + 1000] for i in range(0, len(contracts), 1000)] tokens_df = _process_contract_chunks( mgr=mgr, base_filename="missing_tokens_df_", - chunks=get_contract_chunks(contracts), + chunks=chunks, dirname=dirname, filename="missing_tokens_df.csv", subset=["address"], @@ -492,7 +466,7 @@ def async_update_pools_from_contracts(mgr: Any, current_block: int): ) # update the pool_data from events - update_pools_from_events(-1, mgr, all_events) + update_pools_from_events(-1, mgr, mgr.pools_to_add_from_contracts) mgr.cfg.logger.info( f"Async Updating pools from contracts took {(time.time() - start_time):0.4f} seconds" diff --git a/fastlane_bot/events/async_utils.py b/fastlane_bot/events/async_utils.py deleted file mode 100644 index dbad98382..000000000 --- a/fastlane_bot/events/async_utils.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -[DOC-TODO-short description of what the file does, max 80 chars] - -[DOC-TODO-OPTIONAL-longer description in rst format] - ---- -(c) Copyright Bprotocol foundation 2023-24. -All rights reserved. -Licensed under MIT. -""" -import os -from typing import Any, List, Dict - -from web3 import AsyncWeb3 - -from fastlane_bot.events.exchanges import exchange_factory - - -def get_contract_chunks(contracts: List[Dict[str, Any]]) -> List[List[Dict[str, Any]]]: - return [contracts[i : i + 1000] for i in range(0, len(contracts), 1000)] - - -def get_abis_and_exchanges(mgr: Any) -> Dict[str, Any]: - abis = {} - for exchange_name, exchange in mgr.exchanges.items(): - abis[exchange_name] = exchange.get_abi() - return abis diff --git a/fastlane_bot/events/interface.py b/fastlane_bot/events/interface.py index 1672cfafc..93529b4a2 100644 --- a/fastlane_bot/events/interface.py +++ b/fastlane_bot/events/interface.py @@ -258,82 +258,18 @@ def remove_unmapped_uniswap_v2_pools(self) -> None: """ Remove unmapped uniswap_v2 pools """ - initial_state = self.state.copy() - self.state = [ - pool - for pool in self.state - if pool["exchange_name"] != "uniswap_v2" - or ( - pool["exchange_name"] in self.cfg.UNI_V2_FORKS - and pool["address"] in self.uniswap_v2_event_mappings - ) + unmapped_uniswap_v2_pools = [ + pool for pool in self.state + if pool["exchange_name"] in self.cfg.UNI_V2_FORKS and pool["address"] not in self.uniswap_v2_event_mappings ] - self.cfg.logger.debug( - f"Removed {len(initial_state) - len(self.state)} unmapped uniswap_v2/sushi pools. {len(self.state)} uniswap_v2/sushi pools remaining" - ) - self.log_umapped_pools_by_exchange(initial_state) - - def remove_unmapped_uniswap_v3_pools(self) -> None: - """ - Remove unmapped uniswap_v2 pools - """ - initial_state = self.state.copy() self.state = [ - pool - for pool in self.state - if pool["exchange_name"] != "uniswap_v3" - or ( - pool["exchange_name"] in self.cfg.UNI_V3_FORKS - and pool["address"] in self.uniswap_v3_event_mappings - ) + pool for pool in self.state + if pool not in unmapped_uniswap_v2_pools ] self.cfg.logger.debug( - f"Removed {len(initial_state) - len(self.state)} unmapped uniswap_v2/sushi pools. {len(self.state)} uniswap_v2/sushi pools remaining" - ) - self.log_umapped_pools_by_exchange(initial_state) - - def log_umapped_pools_by_exchange(self, initial_state): - # Log the total number of pools filtered out for each exchange - self.ConfigObj.logger.debug("Unmapped uniswap_v2/sushi pools:") - unmapped_pools = [pool for pool in initial_state if pool not in self.state] - assert len(unmapped_pools) == len(initial_state) - len(self.state) - # uniswap_v3_unmapped = [ - # pool for pool in unmapped_pools if pool["exchange_name"] == "uniswap_v3" - # ] - # self.log_pool_numbers(uniswap_v3_unmapped, "uniswap_v3") - uniswap_v2_unmapped = [ - pool for pool in unmapped_pools if pool["exchange_name"] == "uniswap_v2" - ] - self.log_pool_numbers(uniswap_v2_unmapped, "uniswap_v2") - sushiswap_v2_unmapped = [ - pool for pool in unmapped_pools if pool["exchange_name"] == "sushiswap_v2" - ] - self.log_pool_numbers(sushiswap_v2_unmapped, "sushiswap_v2") - - def remove_faulty_token_pools(self) -> None: - """ - Remove pools with faulty tokens - """ - self.cfg.logger.debug( - f"Total number of pools. {len(self.state)} before removing faulty token pools" + f"{len(unmapped_uniswap_v2_pools)} unmapped uniswap_v2 pools removed, {len(self.state)} pools remaining" ) - safe_pools = [] - for pool in self.state: - self.cfg.logger.info(pool) - try: - self.get_token(pool["tkn0_address"]) - self.get_token(pool["tkn1_address"]) - safe_pools.append(pool) - except Exception as e: - - self.cfg.logger.warning(f"[events.interface] Exception: {e}") - self.cfg.logger.warning( - f"Removing pool for exchange={pool['pair_name']}, pair_name={pool['pair_name']} token={pool['tkn0_key']} from state for faulty token" - ) - - self.state = safe_pools - def update_state(self, state: List[Dict[str, Any]]) -> None: """ Update the state. @@ -348,12 +284,6 @@ def update_state(self, state: List[Dict[str, Any]]) -> None: if self.state == state: self.cfg.logger.warning("WARNING: State not updated") - def drop_all_tables(self) -> None: - """ - Drop all tables. Deprecated. - """ - raise DeprecationWarning("Method not implemented") - def get_pool_data_with_tokens(self) -> List[PoolAndTokens]: """ Get pool data with tokens as a List diff --git a/fastlane_bot/events/managers/base.py b/fastlane_bot/events/managers/base.py index ca6361fbd..0cdbe3df5 100644 --- a/fastlane_bot/events/managers/base.py +++ b/fastlane_bot/events/managers/base.py @@ -85,7 +85,7 @@ class BaseManager: tokens: List[Dict[str, str]] = field(default_factory=dict) target_tokens: List[str] = field(default_factory=list) tenderly_fork_id: str = None - pools_to_add_from_contracts: List[Tuple[str, str, Any, str, str]] = field( + pools_to_add_from_contracts: List[Any] = field( default_factory=list ) exchange_start_blocks: Dict[str, int] = field(default_factory=dict) diff --git a/fastlane_bot/events/managers/manager.py b/fastlane_bot/events/managers/manager.py index 7b80e66f6..9a0a085f6 100644 --- a/fastlane_bot/events/managers/manager.py +++ b/fastlane_bot/events/managers/manager.py @@ -54,9 +54,7 @@ def update_from_event(self, event: Event): # StrategyCreated events get appended to this list to be processed in the async workflow (see main.py), # to gather any currently unknown fee and token info. Then the event will be reprocessed in this method # and the pool data liquidity will be updated at that time (second pass). - self.pools_to_add_from_contracts.append( - (addr, ex_name, event, key, key_value) - ) + self.pools_to_add_from_contracts.append(event) return if "descr" not in pool_info: @@ -246,8 +244,7 @@ def handle_trading_fee_updated(self): def update_remaining_pools(self): remaining_pools = [] - all_events = [pool[2] for pool in self.pools_to_add_from_contracts] - for event in all_events: + for event in self.pools_to_add_from_contracts: addr = self.web3.to_checksum_address(event.address) ex_name = self.exchange_name_from_event(event) if not ex_name: @@ -258,7 +255,7 @@ def update_remaining_pools(self): pool_info = self.get_pool_info(key, key_value, ex_name) if not pool_info: - remaining_pools.append((addr, ex_name, event, key, key_value)) + remaining_pools.append(event) random.shuffle(remaining_pools) self.pools_to_add_from_contracts = remaining_pools diff --git a/fastlane_bot/events/multicall_utils.py b/fastlane_bot/events/multicall_utils.py index ba56f62f7..06efd5e37 100644 --- a/fastlane_bot/events/multicall_utils.py +++ b/fastlane_bot/events/multicall_utils.py @@ -9,91 +9,11 @@ Licensed under MIT. """ from decimal import Decimal -from typing import Dict, Any -from typing import List, Tuple +from typing import Any, List, Dict from fastlane_bot.config.multicaller import MultiCaller from fastlane_bot.events.pools import CarbonV1Pool -from fastlane_bot.events.pools.base import Pool - -ONE = 2 ** 48 - - -def bit_length(value: int) -> int: - """ - Get the bit length of a value. - - Parameters - ---------- - value: int - The value to get the bit length of. - - Returns - ------- - int - The bit length of the value. - - """ - - return len(bin(value).lstrip("0b")) if value > 0 else 0 - - -def encode_float(value: int) -> int: - """ - Encode a float value. - - Parameters - ---------- - value: int - The value to encode. - - Returns - ------- - int - The encoded value. - - """ - exponent = bit_length(value // ONE) - mantissa = value >> exponent - return mantissa | (exponent * ONE) - - -def encode_rate(value: int) -> int: - """ - Encode a rate value. - - Parameters - ---------- - value: int - The value to encode. - - Returns - ------- - int - The encoded value. - - """ - data = int(value.sqrt() * ONE) - length = bit_length(data // ONE) - return (data >> length) << length - - -def encode_token_price(price: Decimal) -> int: - """ - Encode a token price. - - Parameters - ---------- - price: Decimal - The price to encode. - - Returns - ------- - int - The encoded price. - - """ - return encode_float(encode_rate((price))) +from fastlane_bot.utils import encodeFloat, encodeRate def get_pools_for_exchange(exchange: str, mgr: Any) -> List[Any]: @@ -202,7 +122,6 @@ def extract_params_for_multicall(exchange: str, result: Any, pool_info: Dict, mg params["exchange_name"] = exchange elif exchange == "bancor_pol": p, tkn_balance = result - token_price = encode_token_price(Decimal(p[1]) / Decimal(p[0])) if p is not None else 0 params = { "fee": "0.000", "fee_float": 0.000, @@ -213,7 +132,7 @@ def extract_params_for_multicall(exchange: str, result: Any, pool_info: Dict, mg "y_0": tkn_balance, "z_0": tkn_balance, "A_0": 0, - "B_0": token_price, + "B_0": encodeFloat(encodeRate(Decimal(p[1]) / Decimal(p[0]))) if p is not None else 0, "y_1": 0, "z_1": 0, "A_1": 0, diff --git a/fastlane_bot/events/pools/bancor_pol.py b/fastlane_bot/events/pools/bancor_pol.py index 15091e159..19034e95e 100644 --- a/fastlane_bot/events/pools/bancor_pol.py +++ b/fastlane_bot/events/pools/bancor_pol.py @@ -17,6 +17,7 @@ import web3.exceptions from fastlane_bot.data.abi import ERC20_ABI, BANCOR_POL_ABI +from fastlane_bot.utils import encodeFloat, encodeRate from fastlane_bot.events.pools.base import Pool from ..interfaces.event import Event from _decimal import Decimal @@ -29,7 +30,6 @@ class BancorPolPool(Pool): """ exchange_name: str = "bancor_pol" - ONE = 2**48 contract: Contract = None BANCOR_POL_ADDRESS = "0xD06146D292F9651C1D7cf54A3162791DFc2bEf46" @@ -122,8 +122,6 @@ def update_from_contract( except web3.exceptions.BadFunctionCallOutput: print(f"BadFunctionCallOutput: {tkn0}") - token_price = Decimal(p1) / Decimal(p0) - params = { "fee": "0.000", "fee_float": 0.000, @@ -134,7 +132,7 @@ def update_from_contract( "y_0": tkn_balance, "z_0": tkn_balance, "A_0": 0, - "B_0": int(str(self.encode_token_price(token_price))), + "B_0": encodeFloat(encodeRate(Decimal(p1) / Decimal(p0))), } for key, value in params.items(): self.state[key] = value @@ -175,24 +173,6 @@ def get_erc20_tkn_balance( erc20_contract = w3.eth.contract(abi=ERC20_ABI,address=tkn0) return erc20_contract.caller.balanceOf(contract.address) - - @staticmethod - def bitLength(value): - return len(bin(value).lstrip("0b")) if value > 0 else 0 - - def encodeFloat(self, value): - exponent = self.bitLength(value // self.ONE) - mantissa = value >> exponent - return mantissa | (exponent * self.ONE) - - def encodeRate(self, value): - data = int(value.sqrt() * self.ONE) - length = self.bitLength(data // self.ONE) - return (data >> length) << length - - def encode_token_price(self, price): - return self.encodeFloat(self.encodeRate((price))) - def update_erc20_balance(self, token_contract, address) -> dict: """ returns: the current balance held by the POL contract diff --git a/fastlane_bot/events/utils.py b/fastlane_bot/events/utils.py index f6b97b1b3..d6af77396 100644 --- a/fastlane_bot/events/utils.py +++ b/fastlane_bot/events/utils.py @@ -655,46 +655,6 @@ def get_loglevel(loglevel: str) -> Any: ) -def get_event_filters( - n_jobs: int, mgr: Any, start_block: int, current_block: int -) -> Any: - """ - Creates event filters for the specified block range. - - Parameters - ---------- - n_jobs : int - The number of jobs to run in parallel. - mgr : Any - The manager object. - start_block : int - The starting block number of the event filters. - current_block : int - The current block number of the event filters. - - Returns - ------- - Any - A list of event filters. - """ - bancor_pol_events = ["TradingEnabled", "TokenTraded"] - - # Get for exchanges except POL contract - by_block_events = Parallel(n_jobs=n_jobs, backend="threading")( - delayed(event.create_filter)(fromBlock=start_block, toBlock=current_block) - for event in mgr.events - if event.__name__ not in bancor_pol_events - ) - - # Get all events since the beginning of time for Bancor POL contract - max_num_events = Parallel(n_jobs=n_jobs, backend="threading")( - delayed(event.create_filter)(fromBlock=0, toBlock="latest") - for event in mgr.events - if event.__name__ in bancor_pol_events - ) - return by_block_events + max_num_events - - def get_all_events(n_jobs: int, event_filters: Any) -> List[Any]: """ Fetches all events using the given event filters. @@ -918,36 +878,6 @@ def init_bot(mgr: Any) -> CarbonBot: return bot -def update_pools_from_contracts( - mgr: Any, - n_jobs: int, - rows_to_update: List[int] or List[Hashable], - current_block: int = None, -) -> None: - """ - Updates the pools with the given indices by calling the contracts. - - Parameters - ---------- - mgr : Any - The manager object. - n_jobs : int - The number of jobs to run in parallel. - rows_to_update : List[int] - A list of rows to update. - current_block : int, optional - The current block number, by default None - - """ - Parallel(n_jobs=n_jobs, backend="threading")( - delayed(mgr.update)( - pool_info=mgr.pool_data[idx], - block_number=current_block, - ) - for idx in rows_to_update - ) - - def get_cached_events(mgr: Any, logging_path: str) -> List[Any]: """ Gets the cached events. @@ -981,12 +911,8 @@ def handle_subsequent_iterations( arb_mode: str, bot: CarbonBot, flashloan_tokens: List[str], - randomizer: int, - run_data_validator: bool, target_tokens: List[str] = None, - loop_idx: int = 0, logging_path: str = None, - replay_from_block: int = None, tenderly_uri: str = None, mgr: Any = None, forked_from_block: int = None, @@ -1002,18 +928,10 @@ def handle_subsequent_iterations( The bot object. flashloan_tokens : List[str] A list of flashloan tokens. - randomizer : int - The randomizer. - run_data_validator : bool - Whether to run the data validator. target_tokens : List[str], optional A list of target tokens, by default None - loop_idx : int, optional - The loop index, by default 0 logging_path : str, optional The logging path, by default None - replay_from_block : int, optional - The block number to replay from, by default None tenderly_uri : str, optional The Tenderly URI, by default None mgr : Any @@ -1022,38 +940,30 @@ def handle_subsequent_iterations( The block number to fork from. """ - if loop_idx > 0 or replay_from_block: - # bot.db.handle_token_key_cleanup() - bot.db.remove_unmapped_uniswap_v2_pools() - bot.db.remove_zero_liquidity_pools() - bot.db.remove_unsupported_exchanges() - # bot.db.remove_faulty_token_pools() - # bot.db.remove_pools_with_invalid_tokens() - # bot.db.ensure_descr_in_pool_data() + bot.db.remove_unmapped_uniswap_v2_pools() + bot.db.remove_zero_liquidity_pools() + bot.db.remove_unsupported_exchanges() - # Filter the target tokens - if target_tokens: - bot.db.filter_target_tokens(target_tokens) + # Filter the target tokens + if target_tokens: + bot.db.filter_target_tokens(target_tokens) - # Log the forked_from_block - if forked_from_block: - mgr.cfg.logger.info( - f"[events.utils] Submitting bot.run with forked_from_block: {forked_from_block}, replay_from_block {replay_from_block}" - ) - mgr.cfg.w3 = Web3(Web3.HTTPProvider(tenderly_uri)) - bot.db.cfg.w3 = Web3(Web3.HTTPProvider(tenderly_uri)) - bot.ConfigObj.w3 = Web3(Web3.HTTPProvider(tenderly_uri)) - - # Run the bot - bot.run( - flashloan_tokens=flashloan_tokens, - arb_mode=arb_mode, - run_data_validator=run_data_validator, - randomizer=randomizer, - logging_path=logging_path, - replay_mode=True if replay_from_block else False, - replay_from_block=forked_from_block, + # Log the forked_from_block + if forked_from_block: + mgr.cfg.logger.info( + f"[events.utils] Submitting bot.run with forked_from_block: {forked_from_block}" ) + mgr.cfg.w3 = Web3(Web3.HTTPProvider(tenderly_uri)) + bot.db.cfg.w3 = Web3(Web3.HTTPProvider(tenderly_uri)) + bot.ConfigObj.w3 = Web3(Web3.HTTPProvider(tenderly_uri)) + + # Run the bot + bot.run( + flashloan_tokens=flashloan_tokens, + arb_mode=arb_mode, + logging_path=logging_path, + replay_from_block=forked_from_block, + ) def handle_duplicates(mgr: Any): @@ -1072,55 +982,6 @@ def handle_duplicates(mgr: Any): assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" -def handle_initial_iteration( - backdate_pools: bool, - current_block: int, - last_block: int, - mgr: Any, - n_jobs: int, - start_block: int, -): - """ - Handles the initial iteration of the bot. - - Parameters - ---------- - backdate_pools : bool - Whether to backdate the pools. - current_block : int - The current block number. - last_block : int - The last block number. - mgr : Any - The manager object. - n_jobs : int - The number of jobs to run in parallel. - start_block : int - The starting block number. - - """ - - if last_block == 0: - non_multicall_rows_to_update = mgr.get_rows_to_update(start_block) - - if backdate_pools: - # Remove duplicates - non_multicall_rows_to_update = list(set(non_multicall_rows_to_update)) - - # Parse the rows to update - other_pool_rows = parse_non_multicall_rows_to_update( - mgr, non_multicall_rows_to_update - ) - - for rows in [other_pool_rows]: - update_pools_from_contracts( - mgr, - n_jobs=n_jobs, - rows_to_update=rows, - current_block=current_block, - ) - - def get_tenderly_events( mgr, start_block, @@ -1607,28 +1468,6 @@ def set_network_to_mainnet_if_replay( ) -def append_fork_for_cleanup(forks_to_cleanup: List[str], tenderly_uri: str): - """ - Appends the fork to the forks_to_cleanup list if it is not None. - - Parameters - ---------- - forks_to_cleanup : List[str] - The list of forks to cleanup. - tenderly_uri : str - The tenderly uri. - - Returns - ------- - forks_to_cleanup : List[str] - The list of forks to cleanup. - - """ - if tenderly_uri is not None: - forks_to_cleanup.append(tenderly_uri.split("/")[-1]) - return forks_to_cleanup - - def delete_tenderly_forks(forks_to_cleanup: List[str], mgr: Any) -> List[str]: """ Deletes the forks that were created on Tenderly. @@ -1667,6 +1506,7 @@ def delete_tenderly_forks(forks_to_cleanup: List[str], mgr: Any) -> List[str]: return forks_to_keep + def get_current_block( last_block: int, mgr: Any, diff --git a/fastlane_bot/helpers/carbon_trade_splitter.py b/fastlane_bot/helpers/carbon_trade_splitter.py index 487fe5a36..615b63d93 100644 --- a/fastlane_bot/helpers/carbon_trade_splitter.py +++ b/fastlane_bot/helpers/carbon_trade_splitter.py @@ -55,6 +55,8 @@ def split_carbon_trades(cfg: Config, trade_instructions: List[TradeInstruction]) else: carbon_exchanges[exchange_id] = [tx] + assert len(carbon_exchanges) > 0, f"Carbon trade instruction raw_txs = {trade_instruction.raw_txs}" + for txs in carbon_exchanges.values(): new_trade_instructions.append( TradeInstruction( diff --git a/fastlane_bot/helpers/poolandtokens.py b/fastlane_bot/helpers/poolandtokens.py index 120eaa549..a823ef685 100644 --- a/fastlane_bot/helpers/poolandtokens.py +++ b/fastlane_bot/helpers/poolandtokens.py @@ -9,18 +9,17 @@ __VERSION__ = "1.2" __DATE__ = "05/May/2023" -import decimal import math from _decimal import Decimal from dataclasses import dataclass -from typing import Dict, Any, List, Union +from typing import Dict, Any, List, Union, Tuple from fastlane_bot.config import Config # from fastlane_bot.config import SUPPORTED_EXCHANGES, CARBON_V1_NAME, UNISWAP_V3_NAME from fastlane_bot.helpers.univ3calc import Univ3Calculator from fastlane_bot.tools.cpc import ConstantProductCurve -from fastlane_bot.utils import EncodedOrder +from fastlane_bot.utils import decodeOrder, encodeOrder class SolidlyV2StablePoolsNotSupported(Exception): @@ -283,11 +282,11 @@ def to_cpc(self) -> Union[ConstantProductCurve, List[Any]]: self.fee = float(Decimal(str(self.fee))) if self.exchange_name in self.ConfigObj.UNI_V3_FORKS: out = self._univ3_to_cpc() - elif self.exchange_name in [ - self.ConfigObj.BANCOR_POL_NAME, - ] + self.ConfigObj.CARBON_V1_FORKS: + elif self.exchange_name == self.ConfigObj.BANCOR_POL_NAME: out = self._carbon_to_cpc() - elif self.exchange_name in self.ConfigObj.BALANCER_NAME: + elif self.exchange_name in self.ConfigObj.CARBON_V1_FORKS: + out = self._carbon_to_cpc() + elif self.exchange_name == self.ConfigObj.BALANCER_NAME: out = self._balancer_to_cpc() elif self.exchange_name in self.ConfigObj.SOLIDLY_V2_FORKS: if self.pool_type == "volatile": @@ -409,7 +408,6 @@ def _carbon_to_cpc(self) -> ConstantProductCurve: :A: alternative to pa, pb: A = sqrt(pa) - sqrt(pb) in dy/dy :B: alternative to pa, pb: B = sqrt(pb) in dy/dy :tkny: token y - :isdydx: if True prices in dy/dx, if False in quote direction of the pair *Note that ALL parameters are mandatory, except that EITHER pa, bp OR A, B must be given but not both; we do not correct for incorrect assignment of @@ -420,163 +418,104 @@ def _carbon_to_cpc(self) -> ConstantProductCurve: allow to omit yint (in which case it is set to y, but this does not make a difference for the result) """ - def calculate_parameters(y: Decimal, pa: Decimal, pb: Decimal, pm: Decimal, n: Decimal): - H = pa.sqrt() ** n - L = pb.sqrt() ** n - M = pm.sqrt() ** n - A = H - L - B = L - z = y * (H - L) / (M - L) if M > L else y - return z - - def check_overlap(pa0, pb0, pa1, pb1): - min0, max0 = sorted([pa0, pb0]) - min1, max1 = sorted([1 / pa1, 1 / pb1]) - prices_overlap = max(min0, min1) < min(max0, max1) - return prices_overlap - # if idx == 0, use the first curve, otherwise use the second curve. change the numerical values to Decimal - lst = [] - errors = [] - strategy_typed_args = [] - for i in [0, 1]: - - S = Decimal(self.A_1) if i == 0 else Decimal(self.A_0) - B = Decimal(self.B_1) if i == 0 else Decimal(self.B_0) + cpc_list = [] + for order in self._carbon_to_cpc_dict()["strategy_orders"]: try: - if B <= 0: - continue - except decimal.InvalidOperation: - continue - y = Decimal(self.y_1) if i == 0 else Decimal(self.y_0) - z = yint = Decimal(self.z_1) if i == 0 else Decimal(self.z_0) - try: - if y <= Decimal("0"): - continue - except decimal.InvalidOperation: - # print( - # f"decimal.InvalidOperation attributes: {self.exchange_name}, {self.pair_name}, {self.cid}, y={y}, y_0={self.y_0}, y_1={self.y_1}, skipping..." - # ) - continue - encoded_order = EncodedOrder( - **{ - "token": self.pair_name.split("/")[i].replace( - self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS - ), - "A": S, - "B": B, - "y": y, - "z": z, - } - ) + cpc_list.append(ConstantProductCurve.from_carbon(**self._convert_to_float(order))) + except Exception as e: + self.ConfigObj.logger.debug(f"[_carbon_to_cpc] order {order} error {e}") + return cpc_list + + def _carbon_to_cpc_dict(self) -> dict: + encoded_orders = [ + { + "y": int(self.y_1), + "z": int(self.z_1), + "A": int(self.A_1), + "B": int(self.B_1), + }, + { + "y": int(self.y_0), + "z": int(self.z_0), + "A": int(self.A_0), + "B": int(self.B_0), + }, + ] - def decimal_converter(idx): - if idx == 0: - dec0 = self.tkn0_decimals - dec1 = self.tkn1_decimals - else: - dec0 = self.tkn1_decimals - dec1 = self.tkn0_decimals - return Decimal(str(10 ** (dec0 - dec1))) - - decimal_converter = decimal_converter(i) - - p_start = Decimal(encoded_order.p_start) * decimal_converter - p_marg = Decimal(encoded_order.p_marg) * decimal_converter - p_end = Decimal(encoded_order.p_end) * decimal_converter - yint = Decimal(yint) / ( - Decimal("10") ** [self.tkn1_decimals, self.tkn0_decimals][i] - ) - y = Decimal(y) / ( - Decimal("10") ** [self.tkn1_decimals, self.tkn0_decimals][i] - ) - is_limit_order = p_start==p_end - - tkny = 1 if i == 0 else 0 - typed_args = { - "cid": f"{self.cid}-{i}" - if self.exchange_name in self.ConfigObj.CARBON_V1_FORKS - else self.cid, - "yint": yint, - "y": y, - "pb": p_end, - "p_marg": p_marg, # deleted later since not supported by from_carbon() - "pa": p_start, - "is_limit_order": is_limit_order, # deleted later since not supported by from_carbon() - "tkny": self.pair_name.split("/")[tkny].replace( - self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS - ), - "pair": self.pair_name.replace(self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS), - "params": {"exchange": self.exchange_name}, + decoded_orders = [decodeOrder(encoded_order) for encoded_order in encoded_orders] + + decimals = [ + 10 ** self.tkn1_decimals, + 10 ** self.tkn0_decimals, + ] + + converters = [ + Decimal(10) ** (self.tkn0_decimals - self.tkn1_decimals), + Decimal(10) ** (self.tkn1_decimals - self.tkn0_decimals), + ] + + is_carbon = self.exchange_name in self.ConfigObj.CARBON_V1_FORKS + pair = self.pair_name.replace(self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS) + tokens = pair.split("/") + + strategy_orders = [ + { + "cid": f"{self.cid}-{i}" if is_carbon else self.cid, + "yint": Decimal(encoded_orders[i]["z"]) / decimals[i], + "y": Decimal(encoded_orders[i]["y"]) / decimals[i], + "pb": decoded_orders[i]["lowestRate"] * converters[i], + "pa": decoded_orders[i]["highestRate"] * converters[i], + "tkny": tokens[1 - i], + "pair": pair, "fee": self.fee, "descr": self.descr, "params": self._params, } + for i in [0, 1] if encoded_orders[i]["y"] > 0 and encoded_orders[i]["B"] > 0 + ] - strategy_typed_args += [typed_args] + pm_within_range = False + no_limit_orders = False + prices_overlap = False # Only overlapping strategies are selected for modification - if len(strategy_typed_args) == 2: - - is_overlapping = False - pmarg_threshold = Decimal("0.01") # 1% # WARNING using this condition alone can included stable/stable pairs incidently - - # evaluate that the marginal prices are within the pmarg_threshold - pmarg0, pmarg1 = [x['p_marg'] for x in strategy_typed_args] - pmarg0_inv = 1/pmarg0 # one of the orders must be flipped since prices are always dy/dx - but must flip same geomean_pmarg later - percent_component = pmarg_threshold * max(pmarg0_inv, pmarg1) - percent_component_met = abs(pmarg0_inv - pmarg1) <= percent_component + if len(strategy_orders) == 2: + p_marg_0 = (decoded_orders[0]["marginalRate"] * converters[0]) ** -1 + p_marg_1 = (decoded_orders[1]["marginalRate"] * converters[1]) ** +1 - # overlapping strategies by defintion cannot have A=0 i.e. there must be no limit orders - no_limit_orders = (strategy_typed_args[0]['is_limit_order'] == False) and (strategy_typed_args[1]['is_limit_order'] == False) + # check if the marginal prices are within a 1% threshold (may included stable/stable pairs) + pm_within_range = abs(p_marg_0 - p_marg_1) <= max(p_marg_0, p_marg_1) / 100 - # evaluate if the price boundaries pa/pb overlap at one end # TODO check logic and remove duplicate logic if necessary - prices_overlap = check_overlap(strategy_typed_args[0]['pa'], strategy_typed_args[0]['pb'], strategy_typed_args[1]['pa'], strategy_typed_args[1]['pb']) + # verify that the strategy does not consist of any limit orders + no_limit_orders = not any(encoded_order["A"] == 0 for encoded_order in encoded_orders) - # if the threshold is met and neither is a limit order and prices overlap then likely to be overlapping - is_overlapping = percent_component_met and no_limit_orders and prices_overlap + # check if the price boundaries pa/pb overlap at one end + min0, max0 = sorted([strategy_orders[0]["pa"] ** +1, strategy_orders[0]["pb"] ** +1]) + min1, max1 = sorted([strategy_orders[1]["pa"] ** -1, strategy_orders[1]["pb"] ** -1]) + prices_overlap = max(min0, min1) < min(max0, max1) - if is_overlapping: + # if all conditions are met then this is likely an overlapping strategy + if pm_within_range and no_limit_orders and prices_overlap: # calculate the geometric mean - geomean_p_marg = Decimal.sqrt(pmarg0_inv * pmarg1) - - # modify the y_int based on the new geomean to the limit of y #TODO check that this math is correct - typed_args0 = strategy_typed_args[0] - new_yint0 = calculate_parameters(y=typed_args0['y'], pa=typed_args0['pa'], pb=typed_args0['pb'], pm=(1/geomean_p_marg), n=1) - if new_yint0 < typed_args0['y']: - new_yint0 = typed_args0['y'] - typed_args0['yint'] = new_yint0 - - typed_args1 = strategy_typed_args[1] - new_yint1 = calculate_parameters(y=typed_args1['y'], pa=typed_args1['pa'], pb=typed_args1['pb'], pm=(geomean_p_marg), n=1) - if new_yint1 < typed_args1['y']: - new_yint1 = typed_args1['y'] - typed_args1['yint'] = new_yint1 - - # repack the strateg_typed_args - strategy_typed_args = [typed_args0, typed_args1] - - for typed_args in strategy_typed_args: - # delete new args that arent supported by from_carbon() - del typed_args["p_marg"] - del typed_args["is_limit_order"] - try: - if typed_args["y"] > 0: - lst.append( - ConstantProductCurve.from_carbon( - **self._convert_to_float(typed_args) - ) - ) - # else: - # self.ConfigObj.logger.debug(f"empty carbon pool [{typed_args['cid']}]") - except Exception as e: - errmsg = f"[_carbon_to_cpc] error in curve {i} [probably empty: {typed_args}] - [{e}]\n" - self.ConfigObj.logger.debug(errmsg) - errors += [errmsg] - - return lst - + pm = 1 / (p_marg_0 * p_marg_1).sqrt() + # modify the y_int based on the new geomean to the limit of y + for order in strategy_orders: + yint = encodeOrder({ + "liquidity": order["y"], + "lowestRate": order["pb"], + "highestRate": order["pa"], + "marginalRate": pm, + })["z"] + if order["yint"] < yint: + order["yint"] = yint + return { + "strategy_orders": strategy_orders, + "pm_within_range": pm_within_range, + "no_limit_orders": no_limit_orders, + "prices_overlap": prices_overlap, + } def _univ3_to_cpc(self) -> List[Any]: """ diff --git a/fastlane_bot/helpers/routehandler.py b/fastlane_bot/helpers/routehandler.py index 62f3e39f7..98463a592 100644 --- a/fastlane_bot/helpers/routehandler.py +++ b/fastlane_bot/helpers/routehandler.py @@ -24,9 +24,9 @@ import eth_abi import pandas as pd -from .tradeinstruction import TradeInstruction -from ..events.interface import Pool -from ..tools.cpc import T +from fastlane_bot.helpers.tradeinstruction import TradeInstruction +from fastlane_bot.events.interface import Pool +from fastlane_bot.utils import tradeBySourceAmount, tradeByTargetAmount from fastlane_bot.config.constants import AGNI_V3_NAME, BUTTER_V3_NAME, CLEOPATRA_V3_NAME, PANCAKESWAP_V3_NAME, \ ETHEREUM, METAVAULT_V3_NAME @@ -104,16 +104,12 @@ def __post_init__(self): self.ConfigObj = self.trade_instructions[0].ConfigObj @staticmethod - def custom_data_encoder( - agg_trade_instructions: List[TradeInstruction], - ) -> List[TradeInstruction]: + def custom_data_encoder(agg_trade_instructions: List[TradeInstruction]): for i in range(len(agg_trade_instructions)): - instr = agg_trade_instructions[i] - if instr.raw_txs == "[]": - instr.custom_data = "0x" - agg_trade_instructions[i] = instr + if agg_trade_instructions[i].raw_txs == "[]": + agg_trade_instructions[i].custom_data = "0x" else: - tradeInfo = eval(instr.raw_txs) + tradeInfo = eval(agg_trade_instructions[i].raw_txs) tradeActions = [] for trade in tradeInfo: tradeActions += [ @@ -140,9 +136,7 @@ def custom_data_encoder( # Encode the extracted values using the ABI types encoded_data = eth_abi.encode(all_types, values) - instr.custom_data = '0x' + str(encoded_data.hex()) - agg_trade_instructions[i] = instr - return agg_trade_instructions + agg_trade_instructions[i].custom_data = '0x' + str(encoded_data.hex()) def _to_route_struct( self, @@ -322,21 +316,17 @@ def generate_flashloan_struct(self, trade_instructions_objects: List[TradeInstru self.ConfigObj.WBTC_ADDRESS, self.ConfigObj.LINK_ADDRESS ]: - return [ - { - "platformId": 2, - "sourceTokens": [self.wrapped_gas_token_to_native(source_token)], - "sourceAmounts": [abs(trade_instructions_objects[0].amtin_wei)] - } - ] + platform_id = 2 else: - return [ - { - "platformId": 7, - "sourceTokens": [source_token], - "sourceAmounts": [abs(trade_instructions_objects[0].amtin_wei)] - } - ] + platform_id = 7 + + return [ + { + "platformId": platform_id, + "sourceTokens": [source_token], + "sourceAmounts": [abs(trade_instructions_objects[0].amtin_wei)] + } + ] def native_gas_token_to_wrapped(self, tkn: str): """ @@ -348,16 +338,6 @@ def native_gas_token_to_wrapped(self, tkn: str): """ return self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS if tkn == self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS else tkn - def wrapped_gas_token_to_native(self, tkn: str): - """ - This function returns the native gas token address if the input token is the wrapped gas token, otherwise it returns the input token address. - :param tkn: str - The token address - returns: str - The token address, converted to native gas token if the input was the wrapped gas token - """ - return self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS if tkn == self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS else tkn - @staticmethod def _get_trade_dicts_from_objects(trade_instructions: List[TradeInstruction]) -> List[Dict[str, Any]]: return [ @@ -412,26 +392,20 @@ def _slice_dataframe(df: pd.DataFrame) -> List[Tuple[int, pd.DataFrame]]: return list(zip(min_index, slices)) @staticmethod - def aggregate_bancor_v3_trades(calculated_trade_instructions: List[TradeInstruction]): + def aggregate_bancor_v3_trades(trade_instructions: List[TradeInstruction]): """ This function aggregates Bancor V3 trades into a single multi-hop when possible Parameters ---------- - calculated_trade_instructions: List[TradeInstruction] + trade_instructions: List[TradeInstruction] Trade instructions that have already had trades calculated - - - Returns - ------- - calculated_trade_instructions - The trade instructions now with Bancor V3 trades combined into single trades when possible. """ - for idx, trade in enumerate(calculated_trade_instructions): + for idx, trade in enumerate(trade_instructions): if idx > 0: - if trade.exchange_name == calculated_trade_instructions[idx - 1].exchange_name == "bancor_v3": - trade_before = calculated_trade_instructions[idx - 1] + if trade.exchange_name == trade_instructions[idx - 1].exchange_name == "bancor_v3": + trade_before = trade_instructions[idx - 1] # This checks for a two-hop trade through BNT and combines them if trade_before.tknout_address == trade.tknin_address == trade.ConfigObj.BNT_ADDRESS: new_trade_instruction = TradeInstruction(ConfigObj=trade.ConfigObj, cid=trade_before.cid, @@ -441,10 +415,8 @@ def aggregate_bancor_v3_trades(calculated_trade_instructions: List[TradeInstruct pair_sorting="", raw_txs="[]", db=trade.db) new_trade_instruction.tknout_is_native = trade.tknout_is_native new_trade_instruction.tknout_is_wrapped = trade.tknout_is_wrapped - calculated_trade_instructions[idx - 1] = new_trade_instruction - calculated_trade_instructions.pop(idx) - - return calculated_trade_instructions + trade_instructions[idx - 1] = new_trade_instruction + trade_instructions.pop(idx) def aggregate_carbon_trades(self, trade_instructions_objects: List[TradeInstruction]) -> List[TradeInstruction]: """ @@ -533,138 +505,6 @@ def _extract_route_index(self, data: List[Any]) -> List[int]: result.extend(sublist) return result - @staticmethod - def _find_match_for_tkn( - trades: List[TradeInstruction], tkn: str, input="tknin" - ) -> List[Any]: - """ - Refactored find match for trade. - - Parameters - ---------- - trades: List[TradeInstruction] - The trades. - tkn: str - The token. - input: str - The input. - - Returns - ------- - List[Any] - The potential routes. - """ - if input == "tknin": - return [(i, x) for i, x in enumerate(trades) if x.tknout == tkn] - else: - return [(i, x) for i, x in enumerate(trades) if x.tknin == tkn] - - @staticmethod - def _find_match_for_amount( - trades: List[TradeInstruction], amount: Decimal, input="amtin" - ) -> List[Any]: - """ - Refactored find match for amount. - - Parameters - ---------- - trades: List[TradeInstruction] - The trades. - amount: Decimal - The amount. - input: str - The input. - - Returns - ------- - List[Any] - The potential routes. - """ - factor_high = 1.00001 - factor_low = 0.99999 - if input == "amtin": - return [ - (i, x) - for i, x in enumerate(trades) - if (x.amtout >= -amount * factor_high) - & (x.amtout <= -amount * factor_low) - ] - else: - return [ - (i, x) - for i, x in enumerate(trades) - if (x.amtin <= -amount * factor_high) - & (x.amtin >= -amount * factor_low) - ] - - def _calc_amount0( - self, - liquidity: Decimal, - sqrt_price_times_q96_lower_bound: Decimal, - sqrt_price_times_q96_upper_bound: Decimal, - ) -> Decimal: - """ - Refactored calc amount0. - - Parameters - ---------- - liquidity: Decimal - The liquidity. - sqrt_price_times_q96_lower_bound: Decimal - The sqrt price times q96 lower bound. - sqrt_price_times_q96_upper_bound: Decimal - The sqrt price times q96 upper bound. - - Returns - ------- - Decimal - The amount0. - """ - if sqrt_price_times_q96_lower_bound > sqrt_price_times_q96_upper_bound: - sqrt_price_times_q96_lower_bound, sqrt_price_times_q96_upper_bound = ( - sqrt_price_times_q96_upper_bound, - sqrt_price_times_q96_lower_bound, - ) - return Decimal( - liquidity - * (sqrt_price_times_q96_upper_bound - sqrt_price_times_q96_lower_bound) - / sqrt_price_times_q96_upper_bound - / sqrt_price_times_q96_lower_bound - ) - - def _calc_amount1( - self, - liquidity: Decimal, - sqrt_price_times_q96_lower_bound: Decimal, - sqrt_price_times_q96_upper_bound: Decimal, - ) -> Decimal: - """ - Refactored calc amount1. - - Parameters - ---------- - liquidity: Decimal - The liquidity. - sqrt_price_times_q96_lower_bound: Decimal - The sqrt price times q96 lower bound. - sqrt_price_times_q96_upper_bound: Decimal - The sqrt price times q96 upper bound. - - Returns - ------- - Decimal - The amount1. - """ - if sqrt_price_times_q96_lower_bound > sqrt_price_times_q96_upper_bound: - sqrt_price_times_q96_lower_bound, sqrt_price_times_q96_upper_bound = ( - sqrt_price_times_q96_upper_bound, - sqrt_price_times_q96_lower_bound, - ) - return Decimal( - liquidity - * (sqrt_price_times_q96_upper_bound - sqrt_price_times_q96_lower_bound) - ) - def _swap_token0_in( self, amount_in: Decimal, @@ -697,15 +537,11 @@ def _swap_token0_in( Decimal The amount out. """ - amount_in = amount_in * (Decimal(str(1)) - fee) + price_next_n = int(liquidity * self.ConfigObj.Q96 * sqrt_price) + price_next_d = int(liquidity * self.ConfigObj.Q96 + amount_in * (1 - fee) * decimal_tkn0_modifier * sqrt_price) + amount_out = liquidity * abs(sqrt_price - price_next_n // price_next_d) / self.ConfigObj.Q96 - price_next = Decimal( - int(liquidity * self.ConfigObj.Q96 * sqrt_price) - // int(liquidity * self.ConfigObj.Q96 + amount_in * decimal_tkn0_modifier * sqrt_price) - ) - amount_out = self._calc_amount1(liquidity, sqrt_price, price_next) / self.ConfigObj.Q96 - - return Decimal(amount_out / decimal_tkn1_modifier) + return amount_out / decimal_tkn1_modifier def _swap_token1_in( self, @@ -747,17 +583,6 @@ def _swap_token1_in( sqrt_price)) / decimal_tkn0_modifier)) return result - # amount = amount_in * decimal_tkn1_modifier * (Decimal(str(1)) - fee) - # - # price_diff = Decimal((amount_in * decimal_tkn1_modifier * self.ConfigObj.Q96) / liquidity) - # price_next = Decimal(sqrt_price + price_diff) - # - # print(f"p_next: {price_next}") - # amount_out = self._calc_amount0(liquidity, price_next, sqrt_price) / self.ConfigObj.Q96 - # - # print(f"Equation result = {result}, calc0 result={amount_out / decimal_tkn0_modifier}") - # - # return Decimal(amount_out / decimal_tkn0_modifier) def _calc_uniswap_v3_output( self, @@ -808,8 +633,6 @@ def _calc_uniswap_v3_output( decimal_tkn0_modifier = Decimal(str(decimal_tkn0_modifier)) decimal_tkn1_modifier = Decimal(str(decimal_tkn1_modifier)) - # print(f"[_calc_uniswap_v3_output] tkn_in={tkn_in}, tkn_0_address={tkn_0_address}, tkn_1_address={tkn_1_address}, tkn0_in={tkn_in == tkn_0_address}, liquidity={liquidity}, fee={fee}, sqrt_price={sqrt_price}, decimal_tkn0_modifier={decimal_tkn0_modifier}, decimal_tkn1_modifier={decimal_tkn1_modifier}") - return ( self._swap_token0_in( amount_in=amount_in, @@ -830,101 +653,6 @@ def _calc_uniswap_v3_output( ) ) - ONE = 2 ** 48 - - def decodeFloat(self, value): - return (value % self.ONE) << (value // self.ONE) - - def decode(self, value): - return self.decodeFloat(int(value)) / self.ONE - - def decode_decimal_adjustment(self, value: Decimal, tkn_in_decimals: int or str, tkn_out_decimals: int or str): - tkn_in_decimals = int(tkn_in_decimals) - tkn_out_decimals = int(tkn_out_decimals) - return value * Decimal("10") ** ( - (tkn_in_decimals - tkn_out_decimals) / Decimal("2") - ) - - @staticmethod - def _get_input_trade_by_target_carbon( - y, z, A, B, fee, tkns_out: Decimal, trade_by_source: bool = True - ) -> Tuple[Decimal, Decimal]: - """ - Refactored get input trade by target fastlane_bot. - - Parameters - ---------- - y: Decimal - The y. - z: Decimal - The z. - A: Decimal - The A. - B: Decimal - The B. - fee: Decimal - The fee. - tkns_out: Decimal - The tokens out. - - Returns - ------- - Tuple[Decimal, Decimal] - The tokens in and tokens out. - """ - # Fee set to 0 to avoid - fee = Decimal(str(fee)) - tkns_out = min(tkns_out, y) - tkns_in = ( - (tkns_out * z ** 2) / ((A * y + B * z) * (A * y + B * z - A * tkns_out)) - ) - - if not trade_by_source: - # Only taking fee if calculating by trade by target. Otherwise fee will be calculated in trade by source function. - tkns_in = tkns_in * Decimal(1 - fee) - - return tkns_in, tkns_out - - def _get_output_trade_by_source_carbon( - self, y, z, A, B, fee, tkns_in: Decimal - ) -> Tuple[Decimal, Decimal]: - """ - Refactored get output trade by source fastlane_bot. - - Parameters - ---------- - y: Decimal - The y. - z: Decimal - The z. - A: Decimal - The A. - B: Decimal - The B. - fee: Decimal - The fee. - tkns_in: Decimal - The tokens in. - - Returns - ------- - Tuple[Decimal, Decimal] - The tuple of tokens in and tokens out. - """ - - fee = Decimal(str(fee)) - tkns_out = Decimal( - (tkns_in * (B * z + A * y) ** 2) - / (tkns_in * (B * A * z + A ** 2 * y) + z ** 2) - ) - if tkns_out > y: - tkns_in, tkns_out = self._get_input_trade_by_target_carbon( - y=y, z=z, A=A, B=B, fee=fee, tkns_out=y, trade_by_source=True - ) - - tkns_out = tkns_out * (Decimal("1") - fee) - return tkns_in, tkns_out - def _calc_carbon_output( self, curve: Pool, tkn_in: str, tkn_in_decimals: int, tkn_out_decimals: int, amount_in: Decimal ): @@ -943,54 +671,48 @@ def _calc_carbon_output( Returns ------- Decimal + The amount in. The amount out. """ - assert tkn_in != self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, "[routehandler.py _calc_carbon_output] Function does not expect native gas token as input." - amount_in = Decimal(str(amount_in)) - tkn0_address = curve.pair_name.split("/")[0] - tkn1_address = curve.pair_name.split("/")[1] - tkn0_address = self.native_gas_token_to_wrapped(tkn=tkn0_address) - tkn1_address = self.native_gas_token_to_wrapped(tkn=tkn1_address) + key_token = self.native_gas_token_to_wrapped(tkn=tkn_in) + both_tokens = [self.native_gas_token_to_wrapped(tkn=tkn) for tkn in curve.pair_name.split("/")] + assert key_token in both_tokens, f"Token {key_token} does not match tokens {both_tokens} in carbon curve" - # print(f"[_calc_carbon_output] tkn0_address={tkn0_address}, tkn1_address={tkn1_address}, ") + encoded_order = { + both_tokens[0]: { + 'y': int(curve.y_1), + 'z': int(curve.z_1), + 'A': int(curve.A_1), + 'B': int(curve.B_1), + }, + both_tokens[1]: { + 'y': int(curve.y_0), + 'z': int(curve.z_0), + 'A': int(curve.A_0), + 'B': int(curve.B_0), + }, + }[key_token] - assert tkn_in == tkn0_address or tkn_in == tkn1_address, f"Token in: {tkn_in} does not match tokens in Carbon Curve: {tkn0_address} & {tkn1_address}" + target_liquidity = encoded_order['y'] + assert target_liquidity > 0, f"Trade incoming to empty carbon curve: {curve}" - # print(f"[_calc_carbon_output] tkn0_address={tkn0_address}, tkn1_address={tkn1_address}, tkn_int={tkn_in}, using curve0={tkn_in == tkn1_address}") - y, z, A, B = ( - (curve.y_0, curve.z_0, curve.A_0, curve.B_0) - if tkn_in == tkn1_address - else (curve.y_1, curve.z_1, curve.A_1, curve.B_1) - ) + fee = 1 - Decimal(curve.fee_float) - if A is None: - A = 0 + source_scale = 10 ** tkn_in_decimals + target_scale = 10 ** tkn_out_decimals - # print('[_calc_carbon_output] before decode: ', y, z, A, B) - A = self.decode_decimal_adjustment(value=Decimal(str(self.decode(A))), tkn_in_decimals=tkn_in_decimals, - tkn_out_decimals=tkn_out_decimals) - B = self.decode_decimal_adjustment(value=Decimal(str(self.decode(B))), tkn_in_decimals=tkn_in_decimals, - tkn_out_decimals=tkn_out_decimals) - y = Decimal(y) / Decimal("10") ** Decimal(str(tkn_out_decimals)) - z = Decimal(z) / Decimal("10") ** Decimal(str(tkn_out_decimals)) - # print('[_calc_carbon_output] after decode: ', y, z, A, B) - assert y > 0, f"Trade incoming to empty Carbon curve: {curve}" + source_amount = int(amount_in * source_scale) + target_amount = tradeBySourceAmount(source_amount, encoded_order) - # print(f"[_calc_carbon_output] Carbon curve decoded: {y, z, A, B}, fee = {Decimal(curve.fee)}, amount_in={amount_in}") + if target_amount > target_liquidity: + target_amount = target_liquidity + source_amount = tradeByTargetAmount(target_amount, encoded_order) - amt_in, result = self._get_output_trade_by_source_carbon( - y=y, z=z, A=A, B=B, fee=Decimal(curve.fee_float), tkns_in=amount_in - ) - return amt_in, result + real_source_amount = Decimal(source_amount) / source_scale + real_target_amount = Decimal(target_amount) / target_scale * fee - @staticmethod - def _single_trade_result_constant_product( - tokens_in, token0_amt, token1_amt, fee - ) -> Decimal: - return Decimal( - (tokens_in * token1_amt * (1 - Decimal(fee))) / (tokens_in + token0_amt) - ) + return real_source_amount, real_target_amount def _calc_balancer_output(self, curve: Pool, tkn_in: str, tkn_out: str, amount_in: Decimal): """ @@ -1055,38 +777,10 @@ def _calc_balancer_out_given_in(balance_in: Decimal, """ denominator = balance_in + amount_in - base = divUp(balance_in, denominator) # balanceIn.divUp(denominator); - exponent = divDown(weight_in, weight_out) # weightIn.divDown(weightOut); - power = powUp(base, exponent) # base.powUp(exponent); - - return mulDown(balance_out, complement(power)) # balanceOut.mulDown(power.complement()); - - @staticmethod - def _calc_balancer_input_given_output(balance_in: Decimal, - weight_in: Decimal, - balance_out: Decimal, - weight_out: Decimal, - amount_out: Decimal): - """ - This function uses the Balancer swap equation to calculate the token output, given an input. - - :param balance_in: the pool balance of the source token - :param weight_in: the pool weight of the source token - :param balance_out: the pool balance of the target token - :param weight_out: the pool weight of the target token - :param amount_in: the number of source tokens trading into the pool - - returns: - The number of tokens expected to be received by the trade. - - """ - - base = divUp(balance_out, (balance_out - amount_out)) - exponent = divUp(weight_out, weight_in) - power = powUp(base, exponent) - ratio = power - Decimal(1) - result = mulUp(balance_in, ratio) - return result + base = balance_in / denominator if denominator > 0 else 0 + exponent = weight_in / weight_out + power = base ** exponent + return balance_out * (1 - power) def _solve_trade_output( self, curve: Pool, trade: TradeInstruction, amount_in: Decimal = None @@ -1157,14 +851,9 @@ def _solve_trade_output( tkn0_amt = self._from_wei_to_decimals(tkn0_amt, tkn0_dec) tkn1_amt = self._from_wei_to_decimals(tkn1_amt, tkn1_dec) - amount_out = self._single_trade_result_constant_product( - tokens_in=amount_in, - token0_amt=tkn0_amt, - token1_amt=tkn1_amt, - fee=curve.fee_float, - ) + amount_out = (amount_in * tkn1_amt * (1 - Decimal(curve.fee_float))) / (amount_in + tkn0_amt) - amount_out = amount_out * Decimal("0.9999") + amount_out = amount_out * Decimal("0.998") amount_out = TradeInstruction._quantize(amount_out, tkn_out_decimals) amount_in_wei = TradeInstruction._convert_to_wei(amount_in, tkn_in_decimals) amount_out_wei = TradeInstruction._convert_to_wei(amount_out, tkn_out_decimals) @@ -1190,7 +879,7 @@ def calculate_trade_profit( def calculate_trade_outputs( self, trade_instructions: List[TradeInstruction] - ) -> List[TradeInstruction]: + ): """ Refactored calculate trade outputs. @@ -1198,11 +887,6 @@ def calculate_trade_outputs( ---------- trade_instructions: List[Dict[str, Any]] The trade instructions. - - Returns - ------- - List[Dict[str, Any]] - The trade outputs. """ next_amount_in = trade_instructions[0].amtin for idx, trade in enumerate(trade_instructions): @@ -1213,10 +897,6 @@ def calculate_trade_outputs( continue if trade.raw_txs != "[]": data = eval(trade.raw_txs) - total_out = 0 - total_in = 0 - total_in_wei = 0 - total_out_wei = 0 expected_in = trade_instructions[idx].amtin remaining_tkn_in = Decimal(str(next_amount_in)) @@ -1236,7 +916,6 @@ def calculate_trade_outputs( cid = tx["cid"].split("-")[0] curve = trade_instructions[idx].db.get_pool(cid=cid) strategy_id = curve.strategy_id - tknin_address = tx["tknin"] _next_amt_in = Decimal(str(next_amount_in)) * tx["percent_in"] if _next_amt_in > remaining_tkn_in: @@ -1348,45 +1027,9 @@ def calculate_trade_outputs( next_amount_in = amount_out - return trade_instructions - def _from_wei_to_decimals(self, tkn0_amt: Decimal, tkn0_decimals: int) -> Decimal: return Decimal(str(tkn0_amt)) / Decimal("10") ** Decimal(str(tkn0_decimals)) -# TODO: Those functions should probably be private; also -- are they needed at -# all? Most of them seem to be extremely trivial - -def mulUp(a: Decimal, b: Decimal) -> Decimal: - return a * b - - -def divUp(a: Decimal, b: Decimal) -> Decimal: - if a * b == 0: - return Decimal(0) - else: - return a / b - - -def mulDown(a: Decimal, b: Decimal) -> Decimal: - return a * b - - -def divDown(a: Decimal, b: Decimal) -> Decimal: - result = a / b - return result - - -def complement(a: Decimal) -> Decimal: - return Decimal(1 - a) if a < 1 else Decimal(0) - - -def powUp(a: Decimal, b: Decimal) -> Decimal: - return a ** b - - -def powDown(a: Decimal, b: Decimal) -> Decimal: - return a ** b - class BalancerInputTooLargeError(AssertionError): pass diff --git a/fastlane_bot/helpers/txhelpers.py b/fastlane_bot/helpers/txhelpers.py index 0fdf166fc..b44ce98fe 100644 --- a/fastlane_bot/helpers/txhelpers.py +++ b/fastlane_bot/helpers/txhelpers.py @@ -25,7 +25,6 @@ from web3.exceptions import TimeExhausted from fastlane_bot.config import Config -from fastlane_bot.utils import num_format from fastlane_bot.data.abi import ERC20_ABI MAX_UINT256 = 2 ** 256 - 1 @@ -81,13 +80,6 @@ def validate_and_submit_transaction( """ self.cfg.logger.info("[helpers.txhelpers.validate_and_submit_transaction] Validating trade...") - self.cfg.logger.debug( - f"[helpers.txhelpers.validate_and_submit_transaction]:\n" - f"- Routes: {route_struct}\n" - f"- Source amount: {src_amt}\n" - f"- Source token: {src_address}\n" - f"- Expected profit: {num_format(expected_profit_gastkn)} GAS token ({num_format(expected_profit_usd)} USD)\n" - ) if self.cfg.SELF_FUND: fn_name = "fundAndArb" @@ -122,8 +114,8 @@ def validate_and_submit_transaction( self.cfg.logger.info( f"[helpers.txhelpers.validate_and_submit_transaction]:\n" - f"- Expected cost: {num_format(gas_cost_eth)} GAS token ({num_format(gas_cost_usd)} USD)\n" - f"- Expected gain: {num_format(gas_gain_eth)} GAS token ({num_format(gas_gain_usd)} USD)\n" + f"- Expected cost: {gas_cost_eth} GAS token ({gas_cost_usd} USD)\n" + f"- Expected gain: {gas_gain_eth} GAS token ({gas_gain_usd} USD)\n" ) if gas_gain_eth > gas_cost_eth: diff --git a/fastlane_bot/modes/__init__.py b/fastlane_bot/modes/__init__.py index b5f2014df..9af233030 100644 --- a/fastlane_bot/modes/__init__.py +++ b/fastlane_bot/modes/__init__.py @@ -24,13 +24,11 @@ - ``ArbitrageFinderBase`` (``base``): fundamental base class - ``ArbitrageFinderPairwiseBase`` (``base_pairwise``): base class for pairwise arbitrages - - ``FindArbitrageSinglePairwise`` (``pairwise_single``) - - ``FindArbitrageMultiPairwise`` (``pairwise_multi``) - - ``FindArbitrageMultiPairwiseAll`` (``pairwise_multi_all``) - - ``FindArbitrageMultiPairwisePol`` (``pairwise_multi_pol``) + - ``ArbitrageFinderMultiPairwiseAll`` (``pairwise_multi_all``) + - ``ArbitrageFinderMultiPairwisePol`` (``pairwise_multi_pol``) - ``ArbitrageFinderTriangleBase`` (``base_triangle``): base class for triangle arbitrages - - ``ArbitrageFinderTriangleSingle`` (``triangle_single``) - ``ArbitrageFinderTriangleMulti`` (``triangle_multi``) + - ``ArbitrageFinderTriangleMultiComplete`` (``triangle_multi_complete``) - ``ArbitrageFinderTriangleBancor3TwoHop`` (``triangle_bancor_v3_two_hop``) diff --git a/fastlane_bot/modes/base.py b/fastlane_bot/modes/base.py index 0f1cf1a4c..ba2e56429 100644 --- a/fastlane_bot/modes/base.py +++ b/fastlane_bot/modes/base.py @@ -9,310 +9,78 @@ Licensed under MIT. """ import abc -from typing import Any, Tuple, Dict, List, Union from _decimal import Decimal -import pandas as pd - -from fastlane_bot.tools.cpc import T -from fastlane_bot.utils import num_format - +from collections import defaultdict +from typing import Any, List, Dict class ArbitrageFinderBase: """ Base class for all arbitrage finder modes """ - AO_TOKENS = "tokens" - AO_CANDIDATES = "candidates" - - def __init__( - self, - flashloan_tokens, - CCm, - mode="bothin", - result=AO_CANDIDATES, - ConfigObj: Any = None, - arb_mode: str = None, - ): + def __init__(self, flashloan_tokens, CCm, ConfigObj): self.flashloan_tokens = flashloan_tokens self.CCm = CCm - self.mode = mode - self.result = result - self.best_profit = 0 - self.best_src_token = None - self.best_trade_instructions = None - self.best_trade_instructions_df = None - self.best_trade_instructions_dic = None self.ConfigObj = ConfigObj - self.base_exchange = "bancor_v3" if arb_mode == "bancor_v3" else "carbon_v1" + + def find_combos(self) -> List[Any]: + return self.find_arbitrage()["combos"] + + def find_arb_opps(self) -> List[Any]: + return self.find_arbitrage()["arb_opps"] @abc.abstractmethod - def find_arbitrage( - self, - candidates: List[Any] = None, - ops: Tuple = None, - best_profit: float = 0, - profit_src: float = 0, - ) -> Union[List, Tuple]: + def find_arbitrage(self) -> Dict[List[Any], List[Any]]: """ See subclasses for details - Parameters - ---------- - candidates : List[Any], optional - List of candidates, by default None - ops : Tuple, optional - Tuple of operations, by default None - best_profit : float, optional - Best profit so far, by default 0 - profit_src : float, optional - Profit source, by default 0 - Returns ------- - Union[List, Tuple] - If self.result == self.AO_CANDIDATES, it returns a list of candidates. - """ - pass - - def _set_best_ops( - self, - best_profit: float, - ops: Tuple, - profit: float, - src_token: str, - trade_instructions: Any, - trade_instructions_df: pd.DataFrame, - trade_instructions_dic: Dict[str, Any], - ) -> Tuple[float, Tuple]: - """ - Set the best operations. - - Parameters: - - """ - self.ConfigObj.logger.debug("[modes.base._set_best_ops] *************") - self.ConfigObj.logger.debug( - f"[modes.base._set_best_ops] New best profit: {profit}" - ) - - # Update the best profit and source token - best_profit = profit - best_src_token = src_token - - # Update the best trade instructions - best_trade_instructions_df = trade_instructions_df - best_trade_instructions_dic = trade_instructions_dic - best_trade_instructions = trade_instructions - - self.ConfigObj.logger.debug( - f"[modes.base._set_best_ops] best_trade_instructions_df: {best_trade_instructions_df}" - ) - - # Update the optimal operations - ops = ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) - - self.ConfigObj.logger.debug("[modes.base.calculate_profit] *************") - - return best_profit, ops - - def get_prices_simple(self, CCm, tkn0, tkn1): - curve_prices = [(x.params['exchange'],x.descr,x.cid,x.p) for x in CCm.bytknx(tkn0).bytkny(tkn1)] - curve_prices += [(x.params['exchange'],x.descr,x.cid,1/x.p) for x in CCm.bytknx(tkn1).bytkny(tkn0)] - return curve_prices - - # Global constant for 'carbon_v1' order - CARBON_SORTING_ORDER = float('inf') - - # Create a sort order mapping function - def create_sort_order(self, sort_sequence): - # Create a dictionary mapping from sort sequence to indices, except for 'carbon_v1' - return {key: index for index, key in enumerate(sort_sequence) if key != 'carbon_v1'} - - # Define the sort key function separately - def sort_key(self, item, sort_order): - # Check if the item is 'carbon_v1' - if item[0] in self.ConfigObj.CARBON_V1_FORKS: - return self.CARBON_SORTING_ORDER - # Otherwise, use the sort order from the dictionary, or a default high value - return sort_order.get(item[0], self.CARBON_SORTING_ORDER - 1) - - # Define the custom sort function - def custom_sort(self, data, sort_sequence): - sort_order = self.create_sort_order(sort_sequence) - return sorted(data, key=lambda item: self.sort_key(item, sort_order)) - - def calculate_profit( - self, - src_token: str, - profit_src: float, - CCm: Any, - cids: List[str], - profit: int = 0, - ) -> float: - """ - Calculate profit based on the source token. - """ - - best_profit_fl_token = profit_src - if src_token not in [ - self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, - self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, - ]: - if src_token == self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS: - fl_token_with_weth = self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS - else: - fl_token_with_weth = src_token - - sort_sequence = ['bancor_v2','bancor_v3','uniswap_v2','uniswap_v3'] - price_curves = self.get_prices_simple(CCm, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, fl_token_with_weth) - sorted_price_curves = self.custom_sort(price_curves, sort_sequence) - self.ConfigObj.logger.debug(f"[modes.base.calculate_profit sort_sequence] {sort_sequence}") - self.ConfigObj.logger.debug(f"[modes.base.calculate_profit price_curves] {price_curves}") - self.ConfigObj.logger.debug(f"[modes.base.calculate_profit sorted_price_curves] {sorted_price_curves}") - if len(sorted_price_curves)>0: - fltkn_eth_conversion_rate = sorted_price_curves[0][-1] - best_profit_eth = Decimal(str(best_profit_fl_token)) / Decimal(str(fltkn_eth_conversion_rate)) - self.ConfigObj.logger.debug(f"[modes.base.calculate_profit] {src_token, best_profit_fl_token, fltkn_eth_conversion_rate, best_profit_eth}") - else: - self.ConfigObj.logger.error( - f"[modes.base.calculate_profit] Failed to get conversion rate for {fl_token_with_weth} and {self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS}. Raise" - ) - raise - else: - best_profit_eth = best_profit_fl_token - return best_profit_eth - - @staticmethod - def get_netchange(trade_instructions_df: pd.DataFrame) -> List[float]: - """ - Get the net change from the trade instructions. - """ - try: - return trade_instructions_df.iloc[-1] - except Exception: - return [500] # an arbitrary large number - - def handle_candidates( - self, - best_profit: float, - profit: float, - trade_instructions_df: pd.DataFrame, - trade_instructions_dic: Dict[str, Any], - src_token: str, - trade_instructions: Any, - ) -> List[Tuple[float, pd.DataFrame, Dict[str, Any], str, Any]]: - """ - Handle candidate addition based on conditions. - - Parameters: - ---------- - best_profit : float - Best profit - profit : float - Profit - trade_instructions_df : pd.DataFrame - Trade instructions dataframe - trade_instructions_dic : dict - Trade instructions dictionary - src_token : str - Source token - trade_instructions : any - Trade instructions - - Returns: - ------- - candidates : list - Candidates - """ - netchange = self.get_netchange(trade_instructions_df) - condition_zeros_one_token = max(netchange) < 1e-4 - - if ( - condition_zeros_one_token - and profit > self.ConfigObj.DEFAULT_MIN_PROFIT_GAS_TOKEN - ): # candidate regardless if profitable - return [ - ( - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - ] - return [] - - def find_best_operations( - self, - best_profit: float, - ops: Tuple[float, pd.DataFrame, Dict[str, Any], str, Any], - profit: float, - trade_instructions_df: pd.DataFrame, - trade_instructions_dic: Dict[str, Any], - src_token: str, - trade_instructions: Any, - ) -> Tuple[float, Tuple[float, pd.DataFrame, Dict[str, Any], str, Any]]: - """ - Find the best operations based on conditions. - - Parameters: - ---------- - best_profit : float - Best profit - ops : tuple - Operations - profit : float - Profit - trade_instructions_df : pd.DataFrame - Trade instructions dataframe - trade_instructions_dic : dict - Trade instructions dictionary - src_token : str - Source token - trade_instructions : any - Trade instructions - - Returns: - ------- - best_profit : float - Best profit - ops : tuple - Operations - """ - netchange = self.get_netchange(trade_instructions_df) - condition_better_profit = profit > best_profit - condition_zeros_one_token = max(netchange) < 1e-4 - if condition_better_profit and condition_zeros_one_token: - return self._set_best_ops( - best_profit, - ops, - profit, - src_token, - trade_instructions, - trade_instructions_df, - trade_instructions_dic, - ) - return best_profit, ops - - def _check_limit_flashloan_tokens_for_bancor3(self): - """ - Limit the flashloan tokens for bancor v3. + A dictionary with: + - A list of combinations + - A list of arbitrage opportunities """ - fltkns = self.CCm.byparams(exchange="bancor_v3").tknys() - if self.ConfigObj.LIMIT_BANCOR3_FLASHLOAN_TOKENS: - # Filter out tokens that are not in the existing flashloan_tokens list - self.flashloan_tokens = [ - tkn for tkn in fltkns if tkn in self.flashloan_tokens - ] - self.ConfigObj.logger.info( - f"[modes.base._check_limit_flashloan_tokens_for_bancor3] limiting flashloan_tokens to {self.flashloan_tokens}" - ) - else: - self.flashloan_tokens = fltkns + ... + + def get_profit(self, src_token: str, optimization, trade_instructions_dic): + if is_net_change_small(trade_instructions_dic): + profit = self.calculate_profit(src_token, -optimization.result) + if profit.is_finite() and profit > self.ConfigObj.DEFAULT_MIN_PROFIT_GAS_TOKEN: + return profit + return None + + def calculate_profit(self, src_token: str, src_profit: float) -> Decimal: + if src_token not in [self.ConfigObj.NATIVE_GAS_TOKEN_ADDRESS, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS]: + price = self.find_reliable_price(self.CCm, self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS, src_token) + assert price is not None, f"No conversion rate for {self.ConfigObj.WRAPPED_GAS_TOKEN_ADDRESS} and {src_token}" + return Decimal(str(src_profit)) / Decimal(str(price)) + return Decimal(str(src_profit)) + + def get_params(self, container, dst_tokens, src_token): + pstart = {src_token: 1} + for dst_token in dst_tokens: + if dst_token != src_token: + pstart[dst_token] = self.find_reliable_price(container, dst_token, src_token) + if pstart[dst_token] is None: + return None + return {"pstart": pstart} + + def find_reliable_price(self, container, dst_token, src_token): + container1 = container.bytknx(dst_token).bytkny(src_token) + container2 = container.bytknx(src_token).bytkny(dst_token) + for exchange in ["bancor_v2", "bancor_v3", *self.ConfigObj.UNI_V2_FORKS, *self.ConfigObj.UNI_V3_FORKS]: + list1 = [curve.p / 1 for curve in container1.byparams(exchange=exchange).curves] + list2 = [1 / curve.p for curve in container2.byparams(exchange=exchange).curves] + price = (list1 + list2 + [None])[0] + if price is not None: + return price + list1 = [curve.p / 1 for curve in container1.curves] + list2 = [1 / curve.p for curve in container2.curves] + return (list1 + list2 + [None])[0] + +def is_net_change_small(trade_instructions_dic) -> bool: + amounts = defaultdict(float) + for ti in trade_instructions_dic: + amounts[ti["tknin"]] += ti["amtin"] + amounts[ti["tknout"]] += ti["amtout"] + return max(amounts.values()) < 1e-4 diff --git a/fastlane_bot/modes/base_pairwise.py b/fastlane_bot/modes/base_pairwise.py index d5df65ef9..2855bc31c 100644 --- a/fastlane_bot/modes/base_pairwise.py +++ b/fastlane_bot/modes/base_pairwise.py @@ -8,53 +8,42 @@ All rights reserved. Licensed under MIT. """ -import abc -import itertools -from typing import List, Tuple, Any, Union - -from fastlane_bot.modes.base import ArbitrageFinderBase +from typing import Any, List, Dict from fastlane_bot.tools.cpc import CPCContainer - +from fastlane_bot.tools.optimizer import PairOptimizer +from fastlane_bot.modes.base import ArbitrageFinderBase class ArbitrageFinderPairwiseBase(ArbitrageFinderBase): - """ - Base class for pairwise arbitrage finder modes - """ - - @abc.abstractmethod - def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_profit: float = 0, profit_src: float = 0) -> Union[List, Tuple]: - """ - see base.py - """ - pass - - @staticmethod - def get_combos( - CCm: CPCContainer, flashloan_tokens: List[str] - ) -> Tuple[List[Any], List[Any]]: - """ - Get combos for pairwise arbitrage - - Parameters - ---------- - CCm : CPCContainer - Container for all the curves - flashloan_tokens : list - List of flashloan tokens - - Returns - ------- - all_tokens : list - List of all tokens - - """ - all_tokens = CCm.tokens() - flashloan_tokens_intersect = all_tokens.intersection(set(flashloan_tokens)) - combos = [ - (tkn0, tkn1) - for tkn0, tkn1 in itertools.product(all_tokens, flashloan_tokens_intersect) - # tkn1 is always the token being flash loaned - # note that the pair is tkn0/tkn1, ie tkn1 is the quote token - if tkn0 != tkn1 - ] - return all_tokens, combos + def find_arbitrage(self) -> Dict[List[Any], List[Any]]: + arb_opps = [] + combos = self.get_combos() + + for dst_token, src_token in combos: + curves = self.CCm.bypairs(f"{dst_token}/{src_token}").curves + if len(curves) < 2: + continue + + for curve_combos in self.get_curve_combos(curves): + if len(curve_combos) < 2: + continue + + container = CPCContainer(curve_combos) + optimizer = PairOptimizer(container) + params = self.get_params(container, [dst_token], src_token) + + try: + optimization = optimizer.optimize(src_token, params=params) + except Exception as e: + self.ConfigObj.logger.debug(f"[base_pairwise] {e}") + continue + + trade_instructions_dic = optimization.trade_instructions(optimizer.TIF_DICTS) + if trade_instructions_dic is None or len(trade_instructions_dic) < 2: + # Failed to converge + continue + + profit = self.get_profit(src_token, optimization, trade_instructions_dic) + if profit is not None: + arb_opps.append({"profit": profit, "src_token": src_token, "trade_instructions_dic": trade_instructions_dic}) + + return {"combos": combos, "arb_opps": sorted(arb_opps, key=lambda arb_opp: arb_opp["profit"], reverse=True)} diff --git a/fastlane_bot/modes/base_triangle.py b/fastlane_bot/modes/base_triangle.py index 522283b36..7c6eaaf35 100644 --- a/fastlane_bot/modes/base_triangle.py +++ b/fastlane_bot/modes/base_triangle.py @@ -8,308 +8,34 @@ All rights reserved. Licensed under MIT. """ -import abc -import itertools -from typing import List, Any, Tuple, Union - -import pandas as pd - +from typing import Any, List, Dict +from fastlane_bot.tools.cpc import CPCContainer +from fastlane_bot.tools.optimizer import MargPOptimizer from fastlane_bot.modes.base import ArbitrageFinderBase -from fastlane_bot.tools.cpc import T - -def sort_pairs(pairs): - # Clean up the pairs alphabetically - return ["/".join(sorted(pair.split('/'))) for pair in pairs] - -def flatten_nested_items_in_list(nested_list): - # unpack nested items - flattened_list = [] - for items in nested_list: - flat_list = [] - for item in items: - if isinstance(item, list): - flat_list.extend(item) - else: - flat_list.append(item) - flattened_list.append(flat_list) - return flattened_list - -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 = [] - for pair in x_y_pairs: - x,y = pair.split('/') - triangle_groups += [("/".join(sorted([flt,x])), pair, "/".join(sorted([flt,y])))] - return triangle_groups - -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 = [] - for triangle in triangle_groups: - path_len = 0 - has_carbon = False - for pair in triangle: - if all_relevant_pairs_info[pair]['all_counts'] > 0: - path_len += 1 - if all_relevant_pairs_info[pair]['carbon_counts'] > 0: - has_carbon = True - if path_len == 3 and has_carbon == True: - valid_carbon_triangles.append(triangle) - return valid_carbon_triangles class ArbitrageFinderTriangleBase(ArbitrageFinderBase): - """ - Base class for triangular arbitrage finder modes - """ - - @abc.abstractmethod - def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_profit: float = 0, profit_src: float = 0) -> Union[List, Tuple]: - """ - see base.py - """ - pass - - @staticmethod - def get_miniverse( - y_match_curves_not_carbon: List[Any], - base_exchange_curves: List[Any], - x_match_curves_not_carbon: List[Any], - flt: str, - arb_mode: str, - combos: List[Any], - ) -> List[Any]: - """ - Get miniverse for triangular arbitrage - - Parameters - ---------- - y_match_curves_not_carbon : list - List of curves that match the y token and are not on carbon - base_exchange_curves : list - List of curves on the base exchange - x_match_curves_not_carbon : list - List of curves that match the x token and are not on carbon - flt : str - Flashloan token - arb_mode : str - Arbitrage mode - combos : list - List of combos - - Returns - ------- - combos : list - List of combos - - """ - if arb_mode in ["single_triangle", "triangle"]: - miniverses = list( - itertools.product( - y_match_curves_not_carbon, - base_exchange_curves, - x_match_curves_not_carbon, - ) - ) - else: - external_curve_combos = list( - itertools.product(y_match_curves_not_carbon, x_match_curves_not_carbon) - ) - miniverses = [ - base_exchange_curves + list(combo) for combo in external_curve_combos - ] - if miniverses: - combos += list(zip([flt] * len(miniverses), miniverses)) - return combos - - def get_combos( - self, flashloan_tokens: List[str], CCm: Any, arb_mode: str - ) -> Tuple[List[str], List[Any]]: - """ - Get combos for triangular arbitrage + def find_arbitrage(self) -> Dict[List[Any], List[Any]]: + arb_opps = [] + combos = self.get_combos() - Parameters - ---------- - flashloan_tokens : list - List of flashloan tokens - CCm : object - CCm object - arb_mode : str - Arbitrage mode + for src_token, miniverse in combos: + container = CPCContainer(miniverse) + optimizer = MargPOptimizer(container) + params = self.get_params(container, container.tokens(), src_token) - Returns - ------- - combos : list - List of combos - - """ - combos = [] - if arb_mode in ["b3_two_hop"]: - combos = [ - (tkn0, tkn1) - for tkn0, tkn1 in itertools.product(flashloan_tokens, flashloan_tokens) - # note that the pair is tkn0/tkn1, ie tkn1 is the quote token - if tkn0 != tkn1 - ] - else: - all_base_exchange_curves = CCm.byparams(exchange=self.base_exchange).curves - for flt in flashloan_tokens: # may wish to run this for one flt at a time - non_flt_base_exchange_curves = [ - x for x in all_base_exchange_curves if flt not in x.pair - ] - for non_flt_base_exchange_curve in non_flt_base_exchange_curves: - target_tkny = non_flt_base_exchange_curve.tkny - target_tknx = non_flt_base_exchange_curve.tknx - base_exchange_curves = ( - CCm.bypairs(f"{target_tknx}/{target_tkny}") - .byparams(exchange=self.base_exchange) - .curves - ) - if len(base_exchange_curves) == 0: - continue - - base_direction_pair = base_exchange_curves[0].pair - base_direction_one = [curve for curve in base_exchange_curves if curve.pair == base_direction_pair] - base_direction_two = [curve for curve in base_exchange_curves if curve.pair != base_direction_pair] - assert len(base_exchange_curves) == len(base_direction_one) + len(base_direction_two) - y_match_curves = CCm.bypairs( - set(CCm.filter_pairs(onein=target_tknx)) - & set(CCm.filter_pairs(onein=flt)) - ) - x_match_curves = CCm.bypairs( - set(CCm.filter_pairs(onein=target_tkny)) - & set(CCm.filter_pairs(onein=flt)) - ) - - y_match_curves_not_carbon = [ - x - for x in y_match_curves - if x.params.exchange != self.base_exchange - ] - if len(y_match_curves_not_carbon) == 0: - continue - x_match_curves_not_carbon = [ - x - for x in x_match_curves - if x.params.exchange != self.base_exchange - ] - if len(x_match_curves_not_carbon) == 0: - continue - if len(base_direction_one) > 0: - combos = self.get_miniverse( - y_match_curves_not_carbon, - base_direction_one, - x_match_curves_not_carbon, - flt, - arb_mode, - combos, - ) - if len(base_direction_two) > 0: - combos = self.get_miniverse( - y_match_curves_not_carbon, - base_direction_two, - x_match_curves_not_carbon, - flt, - arb_mode, - combos, - ) - return combos - - def get_all_relevant_pairs_info(self, CCm, all_relevant_pairs): - # Get pair info for the cohort to allow decision making at the triangle level - all_relevant_pairs_info = {} - for pair in all_relevant_pairs: - all_relevant_pairs_info[pair] = {} - pair_curves = CCm.bypair(pair) - carbon_curves = [] - non_carbon_curves = [] - for x in pair_curves: - if x.params.exchange in self.ConfigObj.CARBON_V1_FORKS: - carbon_curves += [x] - else: - non_carbon_curves += [x] - all_relevant_pairs_info[pair]['non_carbon_curves'] = non_carbon_curves - all_relevant_pairs_info[pair]['carbon_curves'] = carbon_curves - all_relevant_pairs_info[pair]['curves'] = non_carbon_curves + [carbon_curves] if len(carbon_curves) > 0 else non_carbon_curves # condense carbon curves into a single list - all_relevant_pairs_info[pair]['all_counts'] = len(pair_curves) - all_relevant_pairs_info[pair]['carbon_counts'] = len(carbon_curves) - return all_relevant_pairs_info - - def get_analysis_set_per_flt(self, flt, valid_triangles, all_relevant_pairs_info): - flt_triangle_analysis_set = [] - for triangle in valid_triangles: - multiverse = [all_relevant_pairs_info[pair]['curves'] for pair in triangle] - product_of_triangle = list(itertools.product(multiverse[0], multiverse[1], multiverse[2])) - triangles_to_run = flatten_nested_items_in_list(product_of_triangle) - flt_triangle_analysis_set += list(zip([flt] * len(triangles_to_run), triangles_to_run)) - - self.ConfigObj.logger.debug(f"[base_triangle.get_analysis_set_per_flt] Length of flt_triangle_analysis_set: {flt, len(flt_triangle_analysis_set)}") - return flt_triangle_analysis_set - - def get_comprehensive_triangles( - self, flashloan_tokens: List[str], CCm: Any - ) -> Tuple[List[str], List[Any]]: - """ - Get comprehensive combos for triangular arbitrage - - Parameters - ---------- - flashloan_tokens : list - List of flashloan tokens - CCm : object - CCm object - - Returns - ------- - combos : list - List of combos - - """ - combos = [] - for flt in flashloan_tokens: - - # Get the Carbon pairs - carbon_pairs = sort_pairs(set([x.pair for x in CCm.curves if x.params.exchange in self.ConfigObj.CARBON_V1_FORKS])) - - # Create a set of unique tokens, excluding 'flt' - x_tokens = {token for pair in carbon_pairs for token in pair.split('/') if token != flt} - - # Get relevant pairs containing the flashloan token - flt_x_pairs = sort_pairs([f"{x}/{flt}" for x in x_tokens]) - - # Generate all possible 2-item combinations from the unique tokens that arent the flashloan token - x_y_pairs = sort_pairs(["{}/{}".format(x, y) for x, y in itertools.combinations(x_tokens, 2)]) - - # Note the relevant pairs - all_relevant_pairs = flt_x_pairs + x_y_pairs - self.ConfigObj.logger.debug(f"len(all_relevant_pairs) {len(all_relevant_pairs)}") + try: + optimization = optimizer.optimize(src_token, params=params) + except Exception as e: + self.ConfigObj.logger.debug(f"[base_triangle] {e}") + continue - # Generate triangle groups - triangle_groups = get_triangle_groups(flt, x_y_pairs) - self.ConfigObj.logger.debug(f"len(triangle_groups) {len(triangle_groups)}") + trade_instructions_dic = optimization.trade_instructions(optimizer.TIF_DICTS) + if trade_instructions_dic is None or len(trade_instructions_dic) < 3: + # Failed to converge + continue - # Get pair info for the cohort - all_relevant_pairs_info = self.get_all_relevant_pairs_info(CCm, all_relevant_pairs) - - # Generate valid triangles for the groups base on arb_mode - valid_triangles = get_triangle_groups_stats(triangle_groups, all_relevant_pairs_info) - - # Get [(flt,curves)] analysis set for the flt - flt_triangle_analysis_set = self.get_analysis_set_per_flt(flt, valid_triangles, all_relevant_pairs_info) - - # The entire analysis set for all flts - combos.extend(flt_triangle_analysis_set) - return combos + profit = self.get_profit(src_token, optimization, trade_instructions_dic) + if profit is not None: + arb_opps.append({"profit": profit, "src_token": src_token, "trade_instructions_dic": trade_instructions_dic}) - 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}/{tkn1} price error {e}") - pstart[tkn1] = 1 - return pstart + return {"combos": combos, "arb_opps": sorted(arb_opps, key=lambda arb_opp: arb_opp["profit"], reverse=True)} diff --git a/fastlane_bot/modes/pairwise_multi.py b/fastlane_bot/modes/pairwise_multi.py deleted file mode 100644 index 756345edf..000000000 --- a/fastlane_bot/modes/pairwise_multi.py +++ /dev/null @@ -1,183 +0,0 @@ -""" -Defines the Multi-pairwise arbitrage finder class - -[DOC-TODO-OPTIONAL-longer description in rst format] - ---- -(c) Copyright Bprotocol foundation 2023-24. -All rights reserved. -Licensed under MIT. -""" -from typing import List, Any, Tuple, Union, Hashable - -import pandas as pd - -from fastlane_bot.modes.base_pairwise import ArbitrageFinderPairwiseBase -from fastlane_bot.tools.cpc import CPCContainer -from fastlane_bot.tools.optimizer import MargPOptimizer, PairOptimizer - - -class FindArbitrageMultiPairwise(ArbitrageFinderPairwiseBase): - """ - Multi-pairwise arbitrage finder mode. - """ - - arb_mode = "multi_pairwise" - - def find_arbitrage( - self, - candidates: List[Any] = None, - ops: Tuple = None, - best_profit: float = 0, - profit_src: float = 0, - ) -> Union[List, Tuple]: - """ - see base.py - """ - if self.base_exchange != "carbon_v1": - raise ValueError("base_exchange must be carbon_v1 for `multi` mode") - - if candidates is None: - candidates = [] - - all_tokens, combos = self.get_combos(self.CCm, self.flashloan_tokens) - if self.result == self.AO_TOKENS: - return all_tokens, combos - - candidates = [] - self.ConfigObj.logger.debug( - f"\n ************ combos: {len(combos)} ************\n" - ) - for tkn0, tkn1 in combos: - r = None - CC = self.CCm.bypairs(f"{tkn0}/{tkn1}") - if len(CC) < 2: - continue - carbon_curves = [x for x in CC.curves if x.params.exchange in self.ConfigObj.CARBON_V1_FORKS] - not_carbon_curves = [ - x for x in CC.curves if x.params.exchange not in self.ConfigObj.CARBON_V1_FORKS - ] - curve_combos = [] - - if len(carbon_curves) > 0: - base_direction_pair = carbon_curves[0].pair - base_direction_one = [curve for curve in carbon_curves if curve.pair == base_direction_pair] - base_direction_two = [curve for curve in carbon_curves if curve.pair != base_direction_pair] - - if len(base_direction_one) > 0: - curve_combos += [[curve] + base_direction_one for curve in not_carbon_curves] - - if len(base_direction_two) > 0: - curve_combos += [[curve] + base_direction_two for curve in not_carbon_curves] - - - for curve_combo in curve_combos: - src_token = tkn1 - - if len(curve_combo) < 2: - continue - - try: - (O, profit_src, r, trade_instructions_df,) = self.run_main_flow( - curves=curve_combo, src_token=src_token, tkn0=tkn0, tkn1=tkn1 - ) - - trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - trade_instructions = r.trade_instructions() - - except Exception: - continue - - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 2: - continue - - # Get the cids - cids = [ti["cid"] for ti in trade_instructions_dic] - - # Calculate the profit - profit = self.calculate_profit(src_token, profit_src, self.CCm, cids) - - if str(profit) == "nan": - self.ConfigObj.logger.debug("profit is nan, skipping") - continue - - # Handle candidates based on conditions - candidates += self.handle_candidates( - best_profit, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - # Find the best operations - best_profit, ops = self.find_best_operations( - best_profit, - ops, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - return candidates if self.result == self.AO_CANDIDATES else ops - - def get_wrong_direction_cids( - self, tkn0_into_carbon: bool, trade_instructions_df: pd.DataFrame - ) -> List[Hashable]: - """ - Get the cids of the wrong direction curves - - Parameters - ---------- - tkn0_into_carbon : bool - True if tkn0 is being converted into carbon, False otherwise - trade_instructions_df : pd.DataFrame - The trade instructions dataframe - - Returns - ------- - List[str] - The cids of the wrong direction curves - """ - return [ - idx - for idx, row in trade_instructions_df.iterrows() - if ( - (tkn0_into_carbon and row[0] < 0) - or (not tkn0_into_carbon and row.iloc[0] > 0) - ) - and ("-0" in idx or "-1" in idx) - ] - - @staticmethod - def run_main_flow( - curves: List[Any], src_token: str, tkn0: str, tkn1: str - ) -> Tuple[Any, float, Any, pd.DataFrame]: - """ - Run main flow to find arbitrage. - """ - CC_cc = CPCContainer(curves) - O = PairOptimizer(CC_cc) - pstart = { - tkn0: CC_cc.bypairs(f"{tkn0}/{tkn1}")[0].p - } # this intentionally selects the non_carbon curve - r = O.optimize(src_token, params=dict(pstart=pstart)) - profit_src = -r.result - trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) - return O, profit_src, r, trade_instructions_df - - def process_wrong_direction_pools( - self, curve_combo: List[Any], wrong_direction_cids: List[Hashable] - ) -> [str]: - """ - Process curves with wrong direction pools. - """ - new_curves = [ - curve for curve in curve_combo if curve.cid not in wrong_direction_cids - ] - return new_curves diff --git a/fastlane_bot/modes/pairwise_multi_all.py b/fastlane_bot/modes/pairwise_multi_all.py index 20cdbb46f..ca0ce678e 100644 --- a/fastlane_bot/modes/pairwise_multi_all.py +++ b/fastlane_bot/modes/pairwise_multi_all.py @@ -1,5 +1,5 @@ """ -Defines the Multi-pairwise arbitrage finder class +Defines the multi-pairwise-all arbitrage finder class [DOC-TODO-OPTIONAL-longer description in rst format] @@ -8,179 +8,36 @@ All rights reserved. Licensed under MIT. """ -import itertools -from typing import List, Any, Tuple, Union, Hashable - -import pandas as pd +from typing import Any, List +from itertools import product from fastlane_bot.modes.base_pairwise import ArbitrageFinderPairwiseBase -from fastlane_bot.tools.cpc import CPCContainer -from fastlane_bot.tools.optimizer import MargPOptimizer, PairOptimizer - - -class FindArbitrageMultiPairwiseAll(ArbitrageFinderPairwiseBase): - """ - Multi-pairwise arbitrage finder mode. - """ - - arb_mode = "multi_pairwise_all" - - def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_profit: float = 0, profit_src: float = 0) -> Union[List, Tuple]: - """ - see base.py - """ - - if candidates is None: - candidates = [] - - all_tokens, combos = self.get_combos(self.CCm, self.flashloan_tokens) - if self.result == self.AO_TOKENS: - return all_tokens, combos - #print(f"combos = {combos}") - - candidates = [] - self.ConfigObj.logger.debug( - f"\n ************ combos: {len(combos)} ************\n" - ) - - for tkn0, tkn1 in combos: - r = None - CC = self.CCm.bypairs(f"{tkn0}/{tkn1}") - if len(CC) < 2: - continue - carbon_curves = [x for x in CC.curves if x.params.exchange in self.ConfigObj.CARBON_V1_FORKS] - not_carbon_curves = [ - x for x in CC.curves if x.params.exchange not in self.ConfigObj.CARBON_V1_FORKS - ] - - curve_combos = [[_curve0] + [_curve1] for _curve0 in not_carbon_curves for _curve1 in not_carbon_curves if (_curve0 != _curve1)] - - if len(carbon_curves) > 0: - base_direction_pair = carbon_curves[0].pair - base_direction_one = [curve for curve in carbon_curves if curve.pair == base_direction_pair] - base_direction_two = [curve for curve in carbon_curves if curve.pair != base_direction_pair] - curve_combos = [] - - if len(base_direction_one) > 0: - curve_combos += [[curve] + base_direction_one for curve in not_carbon_curves] - - if len(base_direction_two) > 0: - curve_combos += [[curve] + base_direction_two for curve in not_carbon_curves] - - if len(carbon_curves) >= 2: - curve_combos += [carbon_curves] - - for curve_combo in curve_combos: - src_token = tkn1 - if len(curve_combo) < 2: - continue - try: - ( - O, - profit_src, - r, - trade_instructions_df, - ) = self.run_main_flow(curves=curve_combo, src_token=src_token, tkn0=tkn0, tkn1=tkn1) - except ValueError: - #Optimizer did not converge - continue - - - trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - trade_instructions = r.trade_instructions() - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 2: - continue - # Get the cids - cids = [ti["cid"] for ti in trade_instructions_dic] - - # Calculate the profit - profit = self.calculate_profit(src_token, profit_src, self.CCm, cids) - if str(profit) == "nan": - self.ConfigObj.logger.debug("profit is nan, skipping") - continue - - # Handle candidates based on conditions - candidates += self.handle_candidates( - best_profit, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - # Find the best operations - best_profit, ops = self.find_best_operations( - best_profit, - ops, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - return candidates if self.result == self.AO_CANDIDATES else ops +class ArbitrageFinderMultiPairwiseAll(ArbitrageFinderPairwiseBase): + def get_combos(self) -> List[Any]: + all_tokens = self.CCm.tokens() + flashloan_tokens_intersect = all_tokens.intersection(set(self.flashloan_tokens)) + return [(tkn0, tkn1) for tkn0, tkn1 in product(all_tokens, flashloan_tokens_intersect) if tkn0 != tkn1] - @staticmethod - def get_wrong_direction_cids( - tkn0_into_carbon: bool, trade_instructions_df: pd.DataFrame - ) -> List[Hashable]: - """ - Get the cids of the wrong direction curves + def get_curve_combos(self, curves: List[Any]) -> List[Any]: + carbon_curves = [curve for curve in curves if curve.params.exchange in self.ConfigObj.CARBON_V1_FORKS] + other_curves = [curve for curve in curves if curve.params.exchange not in self.ConfigObj.CARBON_V1_FORKS] - Parameters - ---------- - tkn0_into_carbon : bool - True if tkn0 is being converted into carbon, False otherwise - trade_instructions_df : pd.DataFrame - The trade instructions dataframe + if len(carbon_curves) > 0: + curve_combos = [] - Returns - ------- - List[str] - The cids of the wrong direction curves - """ - return [ - idx - for idx, row in trade_instructions_df.iterrows() - if ( - (tkn0_into_carbon and row.iloc[0] < 0) - or (not tkn0_into_carbon and row.iloc[0] > 0) - ) - and ("-0" in idx or "-1" in idx) - ] + base_dir_one = [curve for curve in carbon_curves if curve.pair == carbon_curves[0].pair] + base_dir_two = [curve for curve in carbon_curves if curve.pair != carbon_curves[0].pair] - @staticmethod - def run_main_flow( - curves: List[Any], src_token: str, tkn0: str, tkn1: str - ) -> Tuple[Any, float, Any, pd.DataFrame]: - """ - Run main flow to find arbitrage. - """ - CC_cc = CPCContainer(curves) - O = PairOptimizer(CC_cc) - pstart = { - tkn0: CC_cc.bypairs(f"{tkn0}/{tkn1}")[0].p - } # this intentionally selects the non_carbon curve + if len(base_dir_one) > 0: + curve_combos += [[curve] + base_dir_one for curve in other_curves] - r = O.optimize(src_token, params=dict(pstart=pstart)) + if len(base_dir_two) > 0: + curve_combos += [[curve] + base_dir_two for curve in other_curves] - profit_src = -r.result - trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) - return O, profit_src, r, trade_instructions_df + if len(carbon_curves) > 1: + curve_combos += [carbon_curves] - @staticmethod - def process_wrong_direction_pools( - curve_combo: List[Any], wrong_direction_cids: List[Hashable] - ) -> [str]: - """ - Process curves with wrong direction pools. - """ - new_curves = [ - curve for curve in curve_combo if curve.cid not in wrong_direction_cids - ] - return new_curves + return curve_combos + return [[curve0, curve1] for curve0 in other_curves for curve1 in other_curves if curve0 != curve1] diff --git a/fastlane_bot/modes/pairwise_multi_pol.py b/fastlane_bot/modes/pairwise_multi_pol.py index 36fc6f1cb..011215f87 100644 --- a/fastlane_bot/modes/pairwise_multi_pol.py +++ b/fastlane_bot/modes/pairwise_multi_pol.py @@ -1,5 +1,5 @@ """ -Defines the Multi-pairwise arbitrage finder class for Bancor POL +Defines the multi-pairwise-pol arbitrage finder class [DOC-TODO-OPTIONAL-longer description in rst format] @@ -8,199 +8,31 @@ All rights reserved. Licensed under MIT. """ -from typing import List, Any, Tuple, Union, Hashable +from typing import Any, List +from itertools import product -import pandas as pd -import itertools from fastlane_bot.modes.base_pairwise import ArbitrageFinderPairwiseBase -from fastlane_bot.tools.cpc import CPCContainer -from fastlane_bot.tools.optimizer import MargPOptimizer, PairOptimizer -from fastlane_bot.tools.cpc import T +class ArbitrageFinderMultiPairwisePol(ArbitrageFinderPairwiseBase): + def get_combos(self) -> List[Any]: + bancor_pol_tkns = self.CCm.byparams(exchange="bancor_pol").tokens() + bancor_pol_tkns = set([tkn for tkn in bancor_pol_tkns if tkn != self.ConfigObj.WETH_ADDRESS]) + return [(tkn0, tkn1) for tkn0, tkn1 in product(bancor_pol_tkns, [self.ConfigObj.WETH_ADDRESS]) if tkn0 != tkn1] -class FindArbitrageMultiPairwisePol(ArbitrageFinderPairwiseBase): - """ - Multi-pairwise arbitrage finder mode for Bancor POL. - """ + def get_curve_combos(self, curves: List[Any]) -> List[Any]: + pol_curves = [curve for curve in curves if curve.params.exchange == "bancor_pol"] + carbon_curves = [curve for curve in curves if curve.params.exchange in self.ConfigObj.CARBON_V1_FORKS] + other_curves = [curve for curve in curves if curve.params.exchange not in ["bancor_pol"] + self.ConfigObj.CARBON_V1_FORKS] + curve_combos = [[curve] + pol_curves for curve in other_curves] - arb_mode = "multi_pairwise_pol" + if len(carbon_curves) > 0: + base_dir_one = [curve for curve in carbon_curves if curve.pair == carbon_curves[0].pair] + base_dir_two = [curve for curve in carbon_curves if curve.pair != carbon_curves[0].pair] - def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_profit: float = 0, profit_src: float = 0) -> Union[List, Tuple]: - """ - see base.py - """ + if len(base_dir_one) > 0: + curve_combos += [[curve] + base_dir_one for curve in pol_curves] - all_tokens, combos = self.get_combos_pol(self.CCm, self.flashloan_tokens) - if self.result == self.AO_TOKENS: - return all_tokens, combos + if len(base_dir_two) > 0: + curve_combos += [[curve] + base_dir_two for curve in pol_curves] - candidates = [] - self.ConfigObj.logger.debug( - f"\n ************ combos: {len(combos)} ************\n" - ) - - for tkn0, tkn1 in combos: - r = None - CC = self.CCm.bypairs(f"{tkn0}/{tkn1}") - if len(CC) < 2: - continue - pol_curves = [x for x in CC.curves if x.params.exchange == "bancor_pol"] - not_bancor_pol_curves = [ - x for x in CC.curves if x.params.exchange not in ["bancor_pol"] + self.ConfigObj.CARBON_V1_FORKS - ] - carbon_curves = [x for x in CC.curves if x.params.exchange in self.ConfigObj.CARBON_V1_FORKS] - curve_combos = [[curve] + pol_curves for curve in not_bancor_pol_curves] - - if len(carbon_curves) > 0: - base_direction_pair = carbon_curves[0].pair - base_direction_one = [curve for curve in carbon_curves if curve.pair == base_direction_pair] - base_direction_two = [curve for curve in carbon_curves if curve.pair != base_direction_pair] - - if len(base_direction_one) > 0: - curve_combos += [[curve] + base_direction_one for curve in pol_curves] - - if len(base_direction_two) > 0: - curve_combos += [[curve] + base_direction_two for curve in pol_curves] - - for curve_combo in curve_combos: - src_token = tkn1 - if len(curve_combo) < 2: - continue - - try: - ( - O, - profit_src, - r, - trade_instructions_df, - ) = self.run_main_flow(curves=curve_combo, src_token=src_token, tkn0=tkn0, tkn1=tkn1) - - trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - trade_instructions = r.trade_instructions() - - except Exception: - continue - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 2: - continue - # Get the cids - cids = [ti["cid"] for ti in trade_instructions_dic] - - # Calculate the profit - profit = self.calculate_profit(src_token, profit_src, self.CCm, cids) - - if str(profit) == "nan": - self.ConfigObj.logger.debug("profit is nan, skipping") - continue - - # Handle candidates based on conditions - candidates += self.handle_candidates( - best_profit, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - # Find the best operations - best_profit, ops = self.find_best_operations( - best_profit, - ops, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - return candidates if self.result == self.AO_CANDIDATES else ops - - def get_wrong_direction_cids( - self, tkn0_into_carbon: bool, trade_instructions_df: pd.DataFrame - ) -> List[Hashable]: - """ - Get the cids of the wrong direction curves - - Parameters - ---------- - tkn0_into_carbon : bool - True if tkn0 is being converted into carbon, False otherwise - trade_instructions_df : pd.DataFrame - The trade instructions dataframe - - Returns - ------- - List[str] - The cids of the wrong direction curves - """ - return [ - idx - for idx, row in trade_instructions_df.iterrows() - if ( - (tkn0_into_carbon and row[0] < 0) - or (not tkn0_into_carbon and row[0] > 0) - ) - and ("-0" in idx or "-1" in idx) - ] - - @staticmethod - def run_main_flow( - curves: List[Any], src_token: str, tkn0: str, tkn1: str - ) -> Tuple[Any, float, Any, pd.DataFrame]: - """ - Run main flow to find arbitrage. - """ - CC_cc = CPCContainer(curves) - O = PairOptimizer(CC_cc) - pstart = { - tkn0: CC_cc.bypairs(f"{tkn0}/{tkn1}")[0].p - } # this intentionally selects the non_carbon curve - r = O.optimize(src_token, params=dict(pstart=pstart)) - profit_src = -r.result - trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) - return O, profit_src, r, trade_instructions_df - - def process_wrong_direction_pools( - self, curve_combo: List[Any], wrong_direction_cids: List[Hashable] - ) -> [str]: - """ - Process curves with wrong direction pools. - """ - new_curves = [ - curve for curve in curve_combo if curve.cid not in wrong_direction_cids - ] - return new_curves - - def get_combos_pol(self, - CCm: CPCContainer, flashloan_tokens: List[str] - ) -> Tuple[List[Any], List[Any]]: - """ - Get combos for pairwise arbitrage specific to Bancor POL - - Parameters - ---------- - CCm : CPCContainer - Container for all the curves - flashloan_tokens : list - List of flashloan tokens - - Returns - ------- - all_tokens : list - List of all tokens - - """ - - bancor_pol_tkns = CCm.byparams(exchange="bancor_pol").tokens() - bancor_pol_tkns = set([tkn for tkn in bancor_pol_tkns if tkn not in [T.ETH, T.WETH]]) - - combos = [ - (tkn0, tkn1) - for tkn0, tkn1 in itertools.product(bancor_pol_tkns, [T.ETH, T.WETH]) - # tkn1 is always the token being flash loaned - # note that the pair is tkn0/tkn1, ie tkn1 is the quote token - if tkn0 != tkn1 - ] - return bancor_pol_tkns, combos \ No newline at end of file + return curve_combos diff --git a/fastlane_bot/modes/pairwise_single.py b/fastlane_bot/modes/pairwise_single.py deleted file mode 100644 index d5128c6b3..000000000 --- a/fastlane_bot/modes/pairwise_single.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -Defines the Single pairwise arbitrage finder class - -[DOC-TODO-OPTIONAL-longer description in rst format] - ---- -(c) Copyright Bprotocol foundation 2023-24. -All rights reserved. -Licensed under MIT. -""" -from typing import List, Any, Tuple, Union - -from tqdm.contrib import itertools - -from fastlane_bot.modes.base_pairwise import ArbitrageFinderPairwiseBase -from fastlane_bot.tools.cpc import CPCContainer -from fastlane_bot.tools.optimizer import MargPOptimizer, PairOptimizer - - -class FindArbitrageSinglePairwise(ArbitrageFinderPairwiseBase): - """ - Single pairwise arbitrage finder mode - """ - - arb_mode = "single_pairwise" - - def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_profit: float = 0, profit_src: float = 0) -> Union[List, Tuple]: - """ - see base.py - """ - - if candidates is None: - candidates = [] - - all_tokens, combos = self.get_combos(self.CCm, self.flashloan_tokens) - - if self.result == self.AO_TOKENS: - return all_tokens, combos - - for tkn0, tkn1 in combos: - r = None - CC = self.CCm.bypairs(f"{tkn0}/{tkn1}") - if len(CC) < 2: - continue - base_exchange_curves = [ - x for x in CC.curves if x.params.exchange == self.base_exchange - ] - not_base_exchange_curves = [ - x for x in CC.curves if x.params.exchange != self.base_exchange - ] - self.ConfigObj.logger.debug( - f"base_exchange: {self.base_exchange}, base_exchange_curves: {len(base_exchange_curves)}, not_base_exchange_curves: {len(not_base_exchange_curves)}" - ) - - curve_combos = list( - itertools.product(not_base_exchange_curves, base_exchange_curves) - ) - - if not curve_combos: - continue - - for curve_combo in curve_combos: - CC_cc = CPCContainer(curve_combo) - O = PairOptimizer(CC_cc) - src_token = tkn1 - try: - pstart = {tkn0: CC_cc.bypairs(f"{tkn0}/{tkn1}")[0].p} - r = O.optimize(src_token, params=dict(pstart=pstart)) - profit_src = -r.result - trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) - trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - trade_instructions = r.trade_instructions() - except Exception as e: - print("[FindArbitrageSinglePairwise] Exception: ", e) - continue - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 2: - continue - # Get the candidate ids - cids = [ti["cid"] for ti in trade_instructions_dic] - - # Calculate the profit - profit = self.calculate_profit(src_token, profit_src, self.CCm, cids) - - if str(profit) == "nan": - self.ConfigObj.logger.debug("profit is nan, skipping") - continue - - # Handle candidates based on conditions - candidates += self.handle_candidates( - best_profit, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - # Find the best operations - best_profit, ops = self.find_best_operations( - best_profit, - ops, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - return candidates if self.result == self.AO_CANDIDATES else ops diff --git a/fastlane_bot/modes/tests/__init__.py b/fastlane_bot/modes/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fastlane_bot/modes/tests/test_pairwise_single.ipynb b/fastlane_bot/modes/tests/test_pairwise_single.ipynb deleted file mode 100644 index 4006398d4..000000000 --- a/fastlane_bot/modes/tests/test_pairwise_single.ipynb +++ /dev/null @@ -1,135 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "84fa264b", - "metadata": { - "ExecuteTime": { - "end_time": "2023-07-06T06:36:12.542528Z", - "start_time": "2023-07-06T06:36:08.293266Z" - } - }, - "outputs": [ - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: 'fastlane_bot/data/static_pool_data.csv'", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mFileNotFoundError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[1], line 8\u001B[0m\n\u001B[1;32m 6\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastlane_bot\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mbot\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m CarbonBot\n\u001B[1;32m 7\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastlane_bot\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mtools\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mcpc\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m ConstantProductCurve \u001B[38;5;28;01mas\u001B[39;00m CPC\n\u001B[0;32m----> 8\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastlane_bot\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mevents\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mexchanges\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m UniswapV2, UniswapV3, CarbonV1, BancorV3\n\u001B[1;32m 9\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastlane_bot\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mevents\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01minterface\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m QueryInterface\n\u001B[1;32m 10\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastlane_bot\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mhelpers\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mpoolandtokens\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m PoolAndTokens\n", - "File \u001B[0;32m~/Local/projects/bancor/carbonbot/fastlane_bot/events/exchanges.py:21\u001B[0m\n\u001B[1;32m 12\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mweb3\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mcontract\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m Contract\n\u001B[1;32m 14\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastlane_bot\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mdata\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mabi\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m (\n\u001B[1;32m 15\u001B[0m UNISWAP_V2_POOL_ABI,\n\u001B[1;32m 16\u001B[0m UNISWAP_V3_POOL_ABI,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 19\u001B[0m BANCOR_V3_POOL_COLLECTION_ABI\n\u001B[1;32m 20\u001B[0m )\n\u001B[0;32m---> 21\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastlane_bot\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mevents\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mpools\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m Pool\n\u001B[1;32m 24\u001B[0m \u001B[38;5;129m@dataclass\u001B[39m\n\u001B[1;32m 25\u001B[0m \u001B[38;5;28;01mclass\u001B[39;00m \u001B[38;5;21;01mExchange\u001B[39;00m(ABC):\n\u001B[1;32m 26\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[1;32m 27\u001B[0m \u001B[38;5;124;03m Base class for exchanges\u001B[39;00m\n\u001B[1;32m 28\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n", - "File \u001B[0;32m~/Local/projects/bancor/carbonbot/fastlane_bot/events/pools.py:524\u001B[0m\n\u001B[1;32m 520\u001B[0m pool_factory\u001B[38;5;241m.\u001B[39mregister_format(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mbancor_v3\u001B[39m\u001B[38;5;124m\"\u001B[39m, BancorV3Pool)\n\u001B[1;32m 521\u001B[0m pool_factory\u001B[38;5;241m.\u001B[39mregister_format(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mcarbon_v1\u001B[39m\u001B[38;5;124m\"\u001B[39m, CarbonV1Pool)\n\u001B[0;32m--> 524\u001B[0m static_data \u001B[38;5;241m=\u001B[39m \u001B[43mpd\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mread_csv\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[38;5;124;43mfastlane_bot/data/static_pool_data.csv\u001B[39;49m\u001B[38;5;124;43m'\u001B[39;49m\u001B[43m)\u001B[49m\u001B[38;5;241m.\u001B[39mto_dict(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mrecords\u001B[39m\u001B[38;5;124m'\u001B[39m)\n\u001B[1;32m 525\u001B[0m sushiswap_v2_pools \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m 526\u001B[0m static_data[idx][\u001B[38;5;124m'\u001B[39m\u001B[38;5;124maddress\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m \u001B[38;5;28mrange\u001B[39m(\u001B[38;5;28mlen\u001B[39m(static_data)) \u001B[38;5;28;01mif\u001B[39;00m static_data[idx][\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mexchange_name\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;241m==\u001B[39m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124msushiswap_v2\u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[1;32m 527\u001B[0m ]\n\u001B[1;32m 528\u001B[0m sushiswap_v3_pools \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m 529\u001B[0m static_data[idx][\u001B[38;5;124m'\u001B[39m\u001B[38;5;124maddress\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m \u001B[38;5;28mrange\u001B[39m(\u001B[38;5;28mlen\u001B[39m(static_data)) \u001B[38;5;28;01mif\u001B[39;00m static_data[idx][\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mexchange_name\u001B[39m\u001B[38;5;124m'\u001B[39m] \u001B[38;5;241m==\u001B[39m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124msushiswap_v3\u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[1;32m 530\u001B[0m ]\n", - "File \u001B[0;32m~/.local/lib/python3.9/site-packages/pandas/util/_decorators.py:211\u001B[0m, in \u001B[0;36mdeprecate_kwarg.._deprecate_kwarg..wrapper\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 209\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m 210\u001B[0m kwargs[new_arg_name] \u001B[38;5;241m=\u001B[39m new_arg_value\n\u001B[0;32m--> 211\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mfunc\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/.local/lib/python3.9/site-packages/pandas/util/_decorators.py:331\u001B[0m, in \u001B[0;36mdeprecate_nonkeyword_arguments..decorate..wrapper\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 325\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mlen\u001B[39m(args) \u001B[38;5;241m>\u001B[39m num_allow_args:\n\u001B[1;32m 326\u001B[0m warnings\u001B[38;5;241m.\u001B[39mwarn(\n\u001B[1;32m 327\u001B[0m msg\u001B[38;5;241m.\u001B[39mformat(arguments\u001B[38;5;241m=\u001B[39m_format_argument_list(allow_args)),\n\u001B[1;32m 328\u001B[0m \u001B[38;5;167;01mFutureWarning\u001B[39;00m,\n\u001B[1;32m 329\u001B[0m stacklevel\u001B[38;5;241m=\u001B[39mfind_stack_level(),\n\u001B[1;32m 330\u001B[0m )\n\u001B[0;32m--> 331\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mfunc\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/.local/lib/python3.9/site-packages/pandas/io/parsers/readers.py:950\u001B[0m, in \u001B[0;36mread_csv\u001B[0;34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, error_bad_lines, warn_bad_lines, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options)\u001B[0m\n\u001B[1;32m 935\u001B[0m kwds_defaults \u001B[38;5;241m=\u001B[39m _refine_defaults_read(\n\u001B[1;32m 936\u001B[0m dialect,\n\u001B[1;32m 937\u001B[0m delimiter,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 946\u001B[0m defaults\u001B[38;5;241m=\u001B[39m{\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mdelimiter\u001B[39m\u001B[38;5;124m\"\u001B[39m: \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m,\u001B[39m\u001B[38;5;124m\"\u001B[39m},\n\u001B[1;32m 947\u001B[0m )\n\u001B[1;32m 948\u001B[0m kwds\u001B[38;5;241m.\u001B[39mupdate(kwds_defaults)\n\u001B[0;32m--> 950\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43m_read\u001B[49m\u001B[43m(\u001B[49m\u001B[43mfilepath_or_buffer\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mkwds\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/.local/lib/python3.9/site-packages/pandas/io/parsers/readers.py:605\u001B[0m, in \u001B[0;36m_read\u001B[0;34m(filepath_or_buffer, kwds)\u001B[0m\n\u001B[1;32m 602\u001B[0m _validate_names(kwds\u001B[38;5;241m.\u001B[39mget(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mnames\u001B[39m\u001B[38;5;124m\"\u001B[39m, \u001B[38;5;28;01mNone\u001B[39;00m))\n\u001B[1;32m 604\u001B[0m \u001B[38;5;66;03m# Create the parser.\u001B[39;00m\n\u001B[0;32m--> 605\u001B[0m parser \u001B[38;5;241m=\u001B[39m \u001B[43mTextFileReader\u001B[49m\u001B[43m(\u001B[49m\u001B[43mfilepath_or_buffer\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[38;5;241;43m*\u001B[39;49m\u001B[43mkwds\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 607\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m chunksize \u001B[38;5;129;01mor\u001B[39;00m iterator:\n\u001B[1;32m 608\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m parser\n", - "File \u001B[0;32m~/.local/lib/python3.9/site-packages/pandas/io/parsers/readers.py:1442\u001B[0m, in \u001B[0;36mTextFileReader.__init__\u001B[0;34m(self, f, engine, **kwds)\u001B[0m\n\u001B[1;32m 1439\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39moptions[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mhas_index_names\u001B[39m\u001B[38;5;124m\"\u001B[39m] \u001B[38;5;241m=\u001B[39m kwds[\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mhas_index_names\u001B[39m\u001B[38;5;124m\"\u001B[39m]\n\u001B[1;32m 1441\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mhandles: IOHandles \u001B[38;5;241m|\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[0;32m-> 1442\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_engine \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_make_engine\u001B[49m\u001B[43m(\u001B[49m\u001B[43mf\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mengine\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/.local/lib/python3.9/site-packages/pandas/io/parsers/readers.py:1735\u001B[0m, in \u001B[0;36mTextFileReader._make_engine\u001B[0;34m(self, f, engine)\u001B[0m\n\u001B[1;32m 1733\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mb\u001B[39m\u001B[38;5;124m\"\u001B[39m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;129;01min\u001B[39;00m mode:\n\u001B[1;32m 1734\u001B[0m mode \u001B[38;5;241m+\u001B[39m\u001B[38;5;241m=\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mb\u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[0;32m-> 1735\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mhandles \u001B[38;5;241m=\u001B[39m \u001B[43mget_handle\u001B[49m\u001B[43m(\u001B[49m\n\u001B[1;32m 1736\u001B[0m \u001B[43m \u001B[49m\u001B[43mf\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1737\u001B[0m \u001B[43m \u001B[49m\u001B[43mmode\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1738\u001B[0m \u001B[43m \u001B[49m\u001B[43mencoding\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43moptions\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mencoding\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43;01mNone\u001B[39;49;00m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1739\u001B[0m \u001B[43m \u001B[49m\u001B[43mcompression\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43moptions\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mcompression\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43;01mNone\u001B[39;49;00m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1740\u001B[0m \u001B[43m \u001B[49m\u001B[43mmemory_map\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43moptions\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mmemory_map\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43;01mFalse\u001B[39;49;00m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1741\u001B[0m \u001B[43m \u001B[49m\u001B[43mis_text\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mis_text\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1742\u001B[0m \u001B[43m \u001B[49m\u001B[43merrors\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43moptions\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mencoding_errors\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mstrict\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1743\u001B[0m \u001B[43m \u001B[49m\u001B[43mstorage_options\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43moptions\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43mstorage_options\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[38;5;28;43;01mNone\u001B[39;49;00m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 1744\u001B[0m \u001B[43m\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 1745\u001B[0m \u001B[38;5;28;01massert\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mhandles \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m 1746\u001B[0m f \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mhandles\u001B[38;5;241m.\u001B[39mhandle\n", - "File \u001B[0;32m~/.local/lib/python3.9/site-packages/pandas/io/common.py:856\u001B[0m, in \u001B[0;36mget_handle\u001B[0;34m(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)\u001B[0m\n\u001B[1;32m 851\u001B[0m \u001B[38;5;28;01melif\u001B[39;00m \u001B[38;5;28misinstance\u001B[39m(handle, \u001B[38;5;28mstr\u001B[39m):\n\u001B[1;32m 852\u001B[0m \u001B[38;5;66;03m# Check whether the filename is to be opened in binary mode.\u001B[39;00m\n\u001B[1;32m 853\u001B[0m \u001B[38;5;66;03m# Binary mode does not support 'encoding' and 'newline'.\u001B[39;00m\n\u001B[1;32m 854\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m ioargs\u001B[38;5;241m.\u001B[39mencoding \u001B[38;5;129;01mand\u001B[39;00m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mb\u001B[39m\u001B[38;5;124m\"\u001B[39m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;129;01min\u001B[39;00m ioargs\u001B[38;5;241m.\u001B[39mmode:\n\u001B[1;32m 855\u001B[0m \u001B[38;5;66;03m# Encoding\u001B[39;00m\n\u001B[0;32m--> 856\u001B[0m handle \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mopen\u001B[39;49m\u001B[43m(\u001B[49m\n\u001B[1;32m 857\u001B[0m \u001B[43m \u001B[49m\u001B[43mhandle\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 858\u001B[0m \u001B[43m \u001B[49m\u001B[43mioargs\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mmode\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 859\u001B[0m \u001B[43m \u001B[49m\u001B[43mencoding\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mioargs\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mencoding\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 860\u001B[0m \u001B[43m \u001B[49m\u001B[43merrors\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43merrors\u001B[49m\u001B[43m,\u001B[49m\n\u001B[1;32m 861\u001B[0m \u001B[43m \u001B[49m\u001B[43mnewline\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[38;5;124;43m\"\u001B[39;49m\u001B[43m,\u001B[49m\n\u001B[1;32m 862\u001B[0m \u001B[43m \u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 863\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m 864\u001B[0m \u001B[38;5;66;03m# Binary mode\u001B[39;00m\n\u001B[1;32m 865\u001B[0m handle \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mopen\u001B[39m(handle, ioargs\u001B[38;5;241m.\u001B[39mmode)\n", - "\u001B[0;31mFileNotFoundError\u001B[0m: [Errno 2] No such file or directory: 'fastlane_bot/data/static_pool_data.csv'" - ] - } - ], - "source": [ - "# coding=utf-8\n", - "\"\"\"\n", - "This module contains the tests for the exchanges classes\n", - "\"\"\"\n", - "from fastlane_bot import Bot, Config\n", - "from fastlane_bot.bot import CarbonBot\n", - "from fastlane_bot.tools.cpc import ConstantProductCurve as CPC\n", - "from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3\n", - "from fastlane_bot.events.interface import QueryInterface\n", - "from fastlane_bot.helpers.poolandtokens import PoolAndTokens\n", - "import pytest\n", - "\n", - "print(\"{0.__name__} v{0.__VERSION__} ({0.__DATE__})\".format(CPC))\n", - "print(\"{0.__name__} v{0.__VERSION__} ({0.__DATE__})\".format(Bot))\n", - "print(\"{0.__name__} v{0.__VERSION__} ({0.__DATE__})\".format(UniswapV2))\n", - "print(\"{0.__name__} v{0.__VERSION__} ({0.__DATE__})\".format(UniswapV3))\n", - "print(\"{0.__name__} v{0.__VERSION__} ({0.__DATE__})\".format(SushiswapV2))\n", - "print(\"{0.__name__} v{0.__VERSION__} ({0.__DATE__})\".format(CarbonV1))\n", - "print(\"{0.__name__} v{0.__VERSION__} ({0.__DATE__})\".format(BancorV3))\n", - "from fastlane_bot.testing import *\n", - "\n", - "plt.style.use('seaborn-dark')\n", - "plt.rcParams['figure.figsize'] = [12,6]\n", - "from fastlane_bot import __VERSION__\n", - "require(\"3.0\", __VERSION__)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a51e5ec2", - "metadata": {}, - "outputs": [], - "source": [ - "C = Config.new(config=Config.CONFIG_MAINNET)\n", - "assert (C.NETWORK == C.NETWORK_MAINNET)\n", - "assert (C.PROVIDER == C.PROVIDER_ALCHEMY)\n", - "setup_bot = CarbonBot(ConfigObj=C)\n", - "pools = [PoolAndTokens(ConfigObj=C, id=87, cid=12590447576074723148144860474975423823949, last_updated=None, last_updated_block=17632852, descr='carbon_v1 BAM-4AaB/ETH-EEeE 0.002', pair_name='BAM-4AaB/ETH-EEeE', exchange_name='carbon_v1', fee='0.002', fee_float=0.002, tkn0_balance=None, tkn1_balance=None, z_0=0, y_0=0, A_0=0, B_0=5232231541229414, z_1=0, y_1=0, A_1=46278427, B_1=1688849860, sqrt_price_q96=None, tick=None, tick_spacing=None, liquidity=None, address='0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', anchor=None, tkn0='BAM', tkn1='ETH', tkn0_address='0x9DB0FB0Aebe6A925B7838D16e3993A3976A64AaB', tkn0_decimals=18, tkn1_address='0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', tkn1_decimals=18, tkn0_key='BAM-4AaB', tkn1_key='ETH-EEeE'), PoolAndTokens(ConfigObj=C, id=88, cid=12930729942995661611608235082407192035408, last_updated=None, last_updated_block=17632852, descr='carbon_v1 stETH-fE84/WETH-6Cc2 0.002', pair_name='stETH-fE84/WETH-6Cc2', exchange_name='carbon_v1', fee='0.002', fee_float=0.002, tkn0_balance=None, tkn1_balance=None, z_0=0, y_0=0, A_0=11537531850305, B_0=267164224749167, z_1=1000000000000000000, y_1=1000000000000000000, A_1=4298139552137, B_1=274347871120865, sqrt_price_q96=None, tick=None, tick_spacing=None, liquidity=None, address='0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1', anchor=None, tkn0='stETH', tkn1='WETH', tkn0_address='0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', tkn0_decimals=18, tkn1_address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tkn1_decimals=18, tkn0_key='stETH-fE84', tkn1_key='WETH-6Cc2'), PoolAndTokens(ConfigObj=C, id=46, cid='0xfc4a2b96bc1cbe2515f7c0bb784c80757a75fd8851a2ff4ecbad69cb1869ddfa', last_updated=10, last_updated_block=17632857, descr='bancor_v3 BNT-FF1C/BAT-87EF 0.000', pair_name='BNT-FF1C/BAT-87EF', exchange_name='bancor_v3', fee='0.000', fee_float=0.0, tkn0_balance=517690761518839476171134, tkn1_balance=1017685474801793344376721, z_0=0, y_0=0, A_0=0, B_0=0, z_1=0, y_1=0, A_1=0, B_1=0, sqrt_price_q96=10, tick=10, tick_spacing=0, liquidity=10, address='0x3506424F91fD33084466F402d5D97f05F8e3b4AF', anchor=10, tkn0='BNT', tkn1='BAT', tkn0_address='0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', tkn0_decimals=18, tkn1_address='0x0D8775F648430679A709E98d2b0Cb6250d2887EF', tkn1_decimals=18, tkn0_key='BNT-FF1C', tkn1_key='BAT-87EF'), PoolAndTokens(ConfigObj=C, id=47, cid='0x1cff7b0388c4532d7def9430fd2685224af19257fbbe12b99d4362f059e7dbd7', last_updated=10, last_updated_block=17632857, descr='bancor_v3 BNT-FF1C/BBS-B430 0.000', pair_name='BNT-FF1C/BBS-B430', exchange_name='bancor_v3', fee='0.000', fee_float=0.0, tkn0_balance=32805970640508809330829, tkn1_balance=1044245786489813870187767, z_0=0, y_0=0, A_0=0, B_0=0, z_1=0, y_1=0, A_1=0, B_1=0, sqrt_price_q96=10, tick=10, tick_spacing=0, liquidity=10, address='0xc12d099be31567add4e4e4d0D45691C3F58f5663', anchor=10, tkn0='BNT', tkn1='BBS', tkn0_address='0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', tkn0_decimals=18, tkn1_address='0xFe459828c90c0BA4bC8b42F5C5D44F316700B430', tkn1_decimals=18, tkn0_key='BNT-FF1C', tkn1_key='BBS-B430'), PoolAndTokens(ConfigObj=C, id=0, cid='0x7c746d3518854384f7a73a6d2da821454319d305a2af4b7015bbf8734a394e49', last_updated=None, last_updated_block=17632854, descr='uniswap_v3 WETH-6Cc2/USDT-1ec7 500', pair_name='WETH-6Cc2/USDT-1ec7', exchange_name='uniswap_v3', fee=500, fee_float=0.0005, tkn0_balance=None, tkn1_balance=None, z_0=0, y_0=0, A_0=0, B_0=0, z_1=0, y_1=0, A_1=0, B_1=0, sqrt_price_q96=3471749971564948777178138, tick=(- 200719), tick_spacing=10, liquidity=9272761710152634655, address='0x11b815efB8f581194ae79006d24E0d814B7697F6', anchor=None, tkn0='WETH', tkn1='USDT', tkn0_address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tkn0_decimals=18, tkn1_address='0xdAC17F958D2ee523a2206206994597C13D831ec7', tkn1_decimals=6, tkn0_key='WETH-6Cc2', tkn1_key='USDT-1ec7'), PoolAndTokens(ConfigObj=C, id=1, cid='0x71fa9967e86d546c3103642dd0876f28a32d4665a1a487d18083421cfd3eff02', last_updated=None, last_updated_block=17632853, descr='uniswap_v3 LDO-1B32/WETH-6Cc2 3000', pair_name='LDO-1B32/WETH-6Cc2', exchange_name='uniswap_v3', fee=3000, fee_float=0.003, tkn0_balance=None, tkn1_balance=None, z_0=0, y_0=0, A_0=0, B_0=0, z_1=0, y_1=0, A_1=0, B_1=0, sqrt_price_q96=2621090548713608947690718187, tick=(- 68179), tick_spacing=60, liquidity=404595565356008216313141, address='0xa3f558aebAecAf0e11cA4b2199cC5Ed341edfd74', anchor=None, tkn0='LDO', tkn1='WETH', tkn0_address='0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32', tkn0_decimals=18, tkn1_address='0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', tkn1_decimals=18, tkn0_key='LDO-1B32', tkn1_key='WETH-6Cc2')]\n", - "state = [pool.__dict__ for pool in pools]\n", - "exchanges = list({ex['exchange_name'] for ex in state})\n", - "mock_qi = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges)\n", - "setup_bot.db = mock_qi" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aaaf7423", - "metadata": {}, - "outputs": [], - "source": [ - "assert (str(type(setup_bot.db)) == \"\")\n", - "pools = setup_bot.db.get_pools()\n", - "assert (len(pools) > 0)\n", - "assert isinstance(pools, list)\n", - "assert all((str(type(pool)).startswith(\"" - pools = setup_bot.db.get_pools() - assert len(pools) > 0 - assert isinstance(pools, list) - assert all( - str(type(pool)).startswith(" Union[List, Tuple]: - """ - see base.py - """ - if self.base_exchange != "bancor_v3": - self.ConfigObj.logger.warning( - f"base_exchange must be bancor_v3 for {self.arb_mode}, setting it to bancor_v3" - ) - self.base_exchange = "bancor_v3" - - self.ConfigObj.logger.info( - f"flashloan_tokens for arb_mode={self.arb_mode} will be overwritten. " - ) - - self._check_limit_flashloan_tokens_for_bancor3() - - if candidates is None: - candidates = [] - - # Get combinations of flashloan tokens - combos = self.get_combos( - self.flashloan_tokens, self.CCm, arb_mode=self.arb_mode - ) + def get_combos(self) -> List[Any]: + combos = [] + CC = self.CCm.byparams(exchange="bancor_v3") - # Get the miniverse combinations - all_miniverses = self.get_miniverse_combos(combos) - - if len(all_miniverses) == 0: - return None - - # Check each source token and miniverse combination - for src_token, miniverse in all_miniverses: - r = None - - try: - # Run main flow with the new set of curves - ( - profit_src, - trade_instructions, - trade_instructions_df, - trade_instructions_dic, - ) = self.run_main_flow(miniverse, src_token) - - except Exception: - continue - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 3: - continue - # Get the candidate ids - cids = [ti["cid"] for ti in trade_instructions_dic] + if self.ConfigObj.LIMIT_BANCOR3_FLASHLOAN_TOKENS: + flashloan_tokens = list(set(CC.tknys()) & set(self.flashloan_tokens)) + else: + flashloan_tokens = CC.tknys() - # Calculate the profit - profit = self.calculate_profit(src_token, profit_src, self.CCm, cids) + bancor_curves_dict = {tkn: CC.bypairs(f"{self.ConfigObj.BNT_ADDRESS}/{tkn}").curves for tkn in flashloan_tokens} - if str(profit) == "nan": - self.ConfigObj.logger.debug("profit is nan, skipping") + for tkn0, tkn1 in [(tkn0, tkn1) for tkn0, tkn1 in product(flashloan_tokens, flashloan_tokens) if tkn0 != tkn1]: + if len(bancor_curves_dict[tkn0]) == 0 or len(bancor_curves_dict[tkn1]) == 0: continue - # Handle candidates based on conditions - candidates += self.handle_candidates( - best_profit, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - # Find the best operations - best_profit, ops = self.find_best_operations( - best_profit, - ops, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - return candidates if self.result == self.AO_CANDIDATES else ops - - def get_tkn(self, pool: Any, tkn_num: int) -> str: - """ - Gets the token ID from a pool object - - Parameters - ---------- - pool: ConstantProductCurve - The ConstantProductCurve object - tkn_num: int - The token number to get, either 0 or 1 - - Returns - ------- - str - """ - return pool.pair.split("/")[tkn_num] - - @staticmethod - def get_fee_safe(fee: int or float): - """ - Fixes the format of the fee if the fee is in PPM format - - Parameters - ---------- - fee: int or float - - Returns - ------- - float - """ - if fee > 1: - fee = fee / 1000000 - return fee - - def get_exact_pools(self, cids: List[str]) -> List[CPCContainer]: - """ - Gets the specific pools that will be used for calculations. It does this inefficiently to preserve the order. - - Parameters - ---------- - cids: List - - Returns - ------- - List - """ - - pools = [curve for curve in self.CCm if curve.cid == cids[0]] - pools += [curve for curve in self.CCm if curve.cid == cids[1]] - pools += [curve for curve in self.CCm if curve.cid == cids[2]] - return pools - - def get_optimal_arb_trade_amts(self, cids: List[str], flt: str) -> float: - """ - Gets the optimal trade 0 amount for a triangular arb cycle + all_curves = list(set(self.CCm.bypairs(f"{tkn0}/{tkn1}")) | set(self.CCm.bypairs(f"{tkn1}/{tkn0}"))) + carbon_curves = [curve for curve in all_curves if curve.params.exchange in self.ConfigObj.CARBON_V1_FORKS] + other_curves = [curve for curve in all_curves if curve.params.exchange not in self.ConfigObj.CARBON_V1_FORKS] + bancor_curves = bancor_curves_dict[tkn0] + bancor_curves_dict[tkn1] - Parameters - ---------- - cids: List - flt: str - - Returns - ------- - float - """ - pools = self.get_exact_pools(cids=cids) - tkn1 = self.get_tkn(pool=pools[0], tkn_num=1) if self.get_tkn(pool=pools[0], tkn_num=1) != flt else self.get_tkn(pool=pools[0], tkn_num=0) - p0t0 = pools[0].x if self.get_tkn(pool=pools[0], tkn_num=0) == flt else pools[0].y - p0t1 = pools[0].y if self.get_tkn(pool=pools[0], tkn_num=0) == flt else pools[0].x - p1t0 = pools[1].x if tkn1 == self.get_tkn(pool=pools[1], tkn_num=0) else pools[1].y - p1t1 = pools[1].y if tkn1 == self.get_tkn(pool=pools[1], tkn_num=0) else pools[1].x - p2t0 = pools[2].x if self.get_tkn(pool=pools[2], tkn_num=0) != flt else pools[2].y - p2t1 = pools[2].y if self.get_tkn(pool=pools[2], tkn_num=0) != flt else pools[2].x - fee0 = self.get_fee_safe(pools[0].fee) - fee1 = self.get_fee_safe(pools[1].fee) - fee2 = self.get_fee_safe(pools[2].fee) - - if pools[1].params.exchange in self.ConfigObj.CARBON_V1_FORKS: - return self.get_exact_input_with_carbon(p0t0, p0t1, p2t0, p2t1, pools[1]) - - return self.max_arb_trade_in_constant_product(p0t0, p0t1, p1t0, p1t1, p2t0, p2t1, fee0=fee0, fee1=fee1, fee2=fee2) - - def get_exact_input_with_carbon(self, p0t0: float, p0t1: float, p2t0: float, p2t1: float, carbon_pool: ConstantProductCurve) -> float: - """ - Gets the optimal trade 0 amount for a triangular arb cycle with a single Carbon order in the middle - - Parameters - ---------- - p0t0: float - p0t1: float - p2t0: float - p2t1: float - carbon_pool: ConstantProductCurve - - Returns - ------- - float - """ - y = carbon_pool.y - z = carbon_pool.z - A = carbon_pool.A - B = carbon_pool.B - C = (B * z + A * y) ** 2 - D = B * A * z + A ** 2 * y - return self.max_arb_trade_in_cp_carbon_cp(p0t0, p0t1, p2t0, p2t1, C, D, z) - - @staticmethod - def max_arb_trade_in_cp_carbon_cp(p0t0: float, p0t1: float, p2t0: float, p2t1: float, C: float, D: float, z: float) -> float: - """ - Equation to solve optimal trade input for a constant product -> Carbon order -> constant product route. - Parameters - ---------- - p0t0: float - p0t1: float - p2t0: float - p2t1: float - C: float - D: float - z: float - Returns - ------- - float - - """ - trade_input = (z * (-p0t0 * p2t0 * z + math.sqrt(C * p0t0 * p2t0 * p0t1 * p2t1))) / (p0t1 * C + p0t1 * D * p2t0 + z ** 2 * p2t0) - return trade_input - - @staticmethod - def max_arb_trade_in_constant_product(p0t0, p0t1, p1t0, p1t1, p2t0, p2t1, fee0, fee1, fee2): - """ - Equation to solve optimal trade input for a constant product -> constant product -> constant product route. - Parameters - ---------- - p0t0: float - p0t1: float - p1t0: float - p1t1: float - p2t0: float - p2t1: float - fee0: float - fee1: float - fee2: float - Returns - ------- - float - - """ - val = (-p1t0*p2t0*p0t0 + (p1t0*p2t0*p0t0*p1t1*p2t1*p0t1*(-fee1*fee2*fee0 + fee1*fee2 + fee1*fee0 - fee1 + fee2*fee0 - fee2 - fee0 + 1)) ** 0.5)/(p1t0*p2t0 - p2t0*p0t1*fee0 + p2t0*p0t1 + p1t1*p0t1*fee1*fee0 - p1t1*p0t1*fee1 - p1t1*p0t1*fee0 + p1t1*p0t1) - return val - - def run_main_flow(self, - miniverse: List, src_token: str - ) -> Tuple[float, Any, Any, Any]: - """ - Run the main flow of the arbitrage finder. - - Parameters - ---------- - miniverse : list - List of curves. - src_token : str - Source token. - - Returns - ------- - tuple - Tuple of profit, trade instructions, trade instructions dataframe and trade instructions dictionary. - - """ - - # Instantiate the container and optimizer objects - CC_cc = CPCContainer(miniverse) - O = MargPOptimizer(CC_cc) - pstart = self.build_pstart(CC_cc, CC_cc.tokens(), src_token) - # Perform the optimization - r = O.optimize(src_token, params=dict(pstart=pstart)) - - # Get the profit in the source token - profit_src = -r.result - - # Get trade instructions in different formats - trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) - trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - trade_instructions = r.trade_instructions() - - return ( - profit_src, - trade_instructions, - trade_instructions_df, - trade_instructions_dic, - ) - - def get_miniverse_combos(self, combos: Iterable) -> List[Tuple[str, List]]: - """ - Get the miniverse combinations for a list of token pairs. - - Parameters - ---------- - combos : list - List of token pairs. - - Returns - ------- - list - List of miniverse combinations. - - """ - all_miniverses = [] - for tkn0, tkn1 in combos: - external_curves = self.CCm.bypairs(f"{tkn0}/{tkn1}") - external_curves += self.CCm.bypairs(f"{tkn1}/{tkn0}") - external_curves = list(set(external_curves)) - carbon_curves = [ - curve - for curve in external_curves - if curve.params.get("exchange") in self.ConfigObj.CARBON_V1_FORKS - ] - external_curves = [ - curve - for curve in external_curves - if curve.params.get("exchange") not in self.ConfigObj.CARBON_V1_FORKS - ] - if not external_curves and not carbon_curves: - continue - - bancor_v3_curve_0 = ( - self.CCm.bypairs(f"{T.BNT}/{tkn0}") - .byparams(exchange="bancor_v3") - .curves - ) - bancor_v3_curve_1 = ( - self.CCm.bypairs(f"{T.BNT}/{tkn1}") - .byparams(exchange="bancor_v3") - .curves - ) - if bancor_v3_curve_0 is None or bancor_v3_curve_1 is None: - continue - if len(bancor_v3_curve_0) == 0 or len(bancor_v3_curve_1) == 0: - continue + base_dir_one = [curve for curve in carbon_curves if curve.pair == carbon_curves[0].pair] + base_dir_two = [curve for curve in carbon_curves if curve.pair != carbon_curves[0].pair] miniverses = [] - if len(external_curves) > 0: - for curve in external_curves: - miniverses += [bancor_v3_curve_0 + bancor_v3_curve_1 + [curve]] - if len(carbon_curves) > 0: - if len(carbon_curves) > 0: - base_direction_pair = carbon_curves[0].pair - base_direction_one = [curve for curve in carbon_curves if curve.pair == base_direction_pair] - base_direction_two = [curve for curve in carbon_curves if curve.pair != base_direction_pair] + if len(other_curves) > 0: + miniverses += [bancor_curves + [curve] for curve in other_curves] - if len(base_direction_one) > 0: - miniverses += [bancor_v3_curve_0 + bancor_v3_curve_1 + base_direction_one] + if len(base_dir_one) > 0: + miniverses += [bancor_curves + base_dir_one] - if len(base_direction_two) > 0: - miniverses += [bancor_v3_curve_0 + bancor_v3_curve_1 + base_direction_two] + if len(base_dir_two) > 0: + miniverses += [bancor_curves + base_dir_two] - miniverses += [bancor_v3_curve_0 + bancor_v3_curve_1 + carbon_curves] + combos += [(tkn1, miniverse) for miniverse in miniverses] - if len(miniverses) > 0: - all_miniverses += list(zip([tkn1] * len(miniverses), miniverses)) - return all_miniverses + return combos diff --git a/fastlane_bot/modes/triangle_multi.py b/fastlane_bot/modes/triangle_multi.py index e6d1f6672..4510d4854 100644 --- a/fastlane_bot/modes/triangle_multi.py +++ b/fastlane_bot/modes/triangle_multi.py @@ -1,5 +1,5 @@ """ -Defines the Triangular arbitrage finder class +Defines the multi-triangle arbitrage finder class [DOC-TODO-OPTIONAL-longer description in rst format] @@ -8,79 +8,73 @@ All rights reserved. Licensed under MIT. """ -from typing import List, Any, Tuple, Union +from typing import Any, List +from itertools import product from fastlane_bot.modes.base_triangle import ArbitrageFinderTriangleBase -from fastlane_bot.tools.cpc import CPCContainer -from fastlane_bot.tools.optimizer import MargPOptimizer - class ArbitrageFinderTriangleMulti(ArbitrageFinderTriangleBase): - """ - Triangular arbitrage finder mode - """ + def get_combos(self) -> List[Any]: + combos = [] + all_carbon_curves = self.CCm.byparams(exchange="carbon_v1").curves + + for flt in self.flashloan_tokens: + non_flt_carbon_curves = [curve for curve in all_carbon_curves if flt not in curve.pair] + for non_flt_carbon_curve in non_flt_carbon_curves: + tkny = non_flt_carbon_curve.tkny + tknx = non_flt_carbon_curve.tknx + + carbon_curves = self.CCm.bypairs(f"{tknx}/{tkny}").byparams(exchange="carbon_v1").curves + if len(carbon_curves) == 0: + continue - arb_mode = "multi_triangle" + filtered_curves = {tkn : set(self.CCm.filter_pairs(onein=tkn)) for tkn in [tknx, tkny, flt]} + y_match_curves = self.CCm.bypairs(filtered_curves[tknx] & filtered_curves[flt]).curves + x_match_curves = self.CCm.bypairs(filtered_curves[tkny] & filtered_curves[flt]).curves - def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_profit: float = 0, profit_src: float = 0) -> Union[List, Tuple]: - """ - see base.py - """ + y_match_other_curves = [curve for curve in y_match_curves if curve.params.exchange != "carbon_v1"] + if len(y_match_other_curves) == 0: + continue - if self.base_exchange != "carbon_v1": - raise ValueError("base_exchange must be carbon_v1 for `multi` mode") + x_match_other_curves = [curve for curve in x_match_curves if curve.params.exchange != "carbon_v1"] + if len(x_match_other_curves) == 0: + continue - if candidates is None: - candidates = [] + base_dir_one = [curve for curve in carbon_curves if curve.pair == carbon_curves[0].pair] + base_dir_two = [curve for curve in carbon_curves if curve.pair != carbon_curves[0].pair] - combos = self.get_combos(self.flashloan_tokens, self.CCm, arb_mode=self.arb_mode) + if len(base_dir_one) > 0: + combos += get_miniverse_combos(y_match_other_curves, x_match_other_curves, base_dir_one, flt) - 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)) - trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - 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.info(f"[triangle multi] {e}") - continue - profit_src = -r.result - - # Get the cids - cids = [ti["cid"] for ti in trade_instructions_dic] - - # Calculate the profit - profit = self.calculate_profit(src_token, profit_src, self.CCm, cids) - if str(profit) == "nan": - self.ConfigObj.logger.debug("profit is nan, skipping") - continue - - # Handle candidates based on conditions - candidates += self.handle_candidates( - best_profit, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - # Find the best operations - best_profit, ops = self.find_best_operations( - best_profit, - ops, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - return candidates if self.result == self.AO_CANDIDATES else ops + if len(base_dir_two) > 0: + combos += get_miniverse_combos(y_match_other_curves, x_match_other_curves, base_dir_two, flt) + + return combos + +def get_miniverse_combos( + y_match_other_curves: List[Any], + x_match_other_curves: List[Any], + base_exchange_curves: List[Any], + flt: str +): + """ + Get miniverse for triangular arbitrage + + Parameters + ---------- + y_match_other_curves : list + List of curves that match the y token and are not on carbon + x_match_other_curves : list + List of curves that match the x token and are not on carbon + base_exchange_curves : list + List of curves on carbon + flt : str + Flashloan token + + Returns + ------- + A list of miniverse combos + """ + curve_combos = list(product(y_match_other_curves, x_match_other_curves)) + miniverses = [base_exchange_curves + list(combo) for combo in curve_combos] + return [(flt, miniverse) for miniverse in miniverses] diff --git a/fastlane_bot/modes/triangle_multi_complete.py b/fastlane_bot/modes/triangle_multi_complete.py new file mode 100644 index 000000000..57012ce5a --- /dev/null +++ b/fastlane_bot/modes/triangle_multi_complete.py @@ -0,0 +1,118 @@ +""" +Defines the multi-triangle-complete arbitrage finder class + +[DOC-TODO-OPTIONAL-longer description in rst format] + +--- +(c) Copyright Bprotocol foundation 2023-24. +All rights reserved. +Licensed under MIT. +""" +from typing import Any, List +from itertools import product, combinations + +from fastlane_bot.modes.base_triangle import ArbitrageFinderTriangleBase + +class ArbitrageFinderTriangleMultiComplete(ArbitrageFinderTriangleBase): + def get_combos(self) -> List[Any]: + combos = [] + + for flt in self.flashloan_tokens: + # Get the Carbon pairs + carbon_pairs = sort_pairs(set([curve.pair for curve in self.CCm.curves if curve.params.exchange in self.ConfigObj.CARBON_V1_FORKS])) + + # Create a set of unique tokens excluding the flashloan token + x_tokens = {token for pair in carbon_pairs for token in pair.split("/") if token != flt} + + # Get relevant pairs containing the flashloan token + flt_x_pairs = sort_pairs([f"{x_token}/{flt}" for x_token in x_tokens]) + + # Generate all possible 2-item combinations from the unique tokens that arent the flashloan token + x_y_pairs = sort_pairs(["{}/{}".format(x, y) for x, y in combinations(x_tokens, 2)]) + + # Note the relevant pairs + all_relevant_pairs = flt_x_pairs + x_y_pairs + self.ConfigObj.logger.debug(f"len(all_relevant_pairs) {len(all_relevant_pairs)}") + + # Generate triangle groups + triangle_groups = get_triangle_groups(flt, x_y_pairs) + self.ConfigObj.logger.debug(f"len(triangle_groups) {len(triangle_groups)}") + + # Get pair info for the cohort + all_relevant_pairs_info = get_all_relevant_pairs_info(self.CCm, all_relevant_pairs, self.ConfigObj.CARBON_V1_FORKS) + + # Generate valid triangles for the groups base on arb_mode + valid_triangles = get_triangle_groups_stats(triangle_groups, all_relevant_pairs_info) + + # Get [(flt,curves)] analysis set for the flashloan token + flt_triangle_analysis_set = get_analysis_set_per_flt(flt, valid_triangles, all_relevant_pairs_info) + + # The entire analysis set for all flashloan tokens + combos.extend(flt_triangle_analysis_set) + + return combos + +def sort_pairs(pairs): + # Clean up the pairs alphabetically + return ["/".join(sorted(pair.split("/"))) for pair in pairs] + +def flatten_nested_items_in_list(nested_list): + # unpack nested items + flattened_list = [] + for items in nested_list: + flat_list = [] + for item in items: + if isinstance(item, list): + flat_list.extend(item) + else: + flat_list.append(item) + flattened_list.append(flat_list) + return flattened_list + +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 = [] + for pair in x_y_pairs: + x, y = pair.split("/") + triangle_groups += [("/".join(sorted([flt, x])), pair, "/".join(sorted([flt, y])))] + return triangle_groups + +def get_all_relevant_pairs_info(CCm, all_relevant_pairs, carbon_v1_forks): + # Get pair info for the cohort to allow decision making at the triangle level + all_relevant_pairs_info = {} + for pair in all_relevant_pairs: + pair_curves = CCm.bypair(pair).curves + carbon_curves = [curve for curve in pair_curves if curve.params.exchange in carbon_v1_forks] + other_curves = [curve for curve in pair_curves if curve.params.exchange not in carbon_v1_forks] + all_relevant_pairs_info[pair] = { + "has_any": len(pair_curves) > 0, + "has_carbon": len(carbon_curves) > 0, + "curves": other_curves + } + if len(carbon_curves) > 0: + base_dir_one = [curve for curve in carbon_curves if curve.pair == carbon_curves[0].pair] + base_dir_two = [curve for curve in carbon_curves if curve.pair != carbon_curves[0].pair] + if len(base_dir_one) > 0: + all_relevant_pairs_info[pair]["curves"].append(base_dir_one) + if len(base_dir_two) > 0: + all_relevant_pairs_info[pair]["curves"].append(base_dir_two) + return all_relevant_pairs_info + +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 = [] + for triangle in triangle_groups: + path_len = sum(all_relevant_pairs_info[pair]["has_any"] for pair in triangle) + has_carbon = any(all_relevant_pairs_info[pair]["has_carbon"] for pair in triangle) + if path_len == 3 and has_carbon == True: + valid_carbon_triangles.append(triangle) + return valid_carbon_triangles + +def get_analysis_set_per_flt(flt, valid_triangles, all_relevant_pairs_info): + flt_triangle_analysis_set = [] + for triangle in valid_triangles: + multiverse = [all_relevant_pairs_info[pair]["curves"] for pair in triangle] + product_of_triangle = list(product(multiverse[0], multiverse[1], multiverse[2])) + triangles_to_run = flatten_nested_items_in_list(product_of_triangle) + flt_triangle_analysis_set += list(zip([flt] * len(triangles_to_run), triangles_to_run)) + return flt_triangle_analysis_set diff --git a/fastlane_bot/modes/triangle_single.py b/fastlane_bot/modes/triangle_single.py deleted file mode 100644 index 3c3b3f825..000000000 --- a/fastlane_bot/modes/triangle_single.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -Defines the Triangle single arbitrage finder class - -[DOC-TODO-OPTIONAL-longer description in rst format] - ---- -(c) Copyright Bprotocol foundation 2023-24. -All rights reserved. -Licensed under MIT. -""" -from typing import Union, List, Tuple, Any - -from fastlane_bot.modes.base_triangle import ArbitrageFinderTriangleBase -from fastlane_bot.tools.cpc import CPCContainer -from fastlane_bot.tools.optimizer import MargPOptimizer - - -class ArbitrageFinderTriangleSingle(ArbitrageFinderTriangleBase): - """ - Triangle single arbitrage finder mode - """ - - arb_mode = "single_triangle" - - def find_arbitrage(self, candidates: List[Any] = None, ops: Tuple = None, best_profit: float = 0, profit_src: float = 0) -> Union[List, Tuple]: - """ - see base.py - """ - - if candidates is None: - candidates = [] - - # Get combinations of flashloan tokens - combos = self.get_combos( - self.flashloan_tokens, self.CCm, arb_mode=self.arb_mode - ) - - # Check each source token and miniverse combination - for src_token, miniverse in combos: - r = None - - # Instantiate the container and optimizer objects - CC_cc = CPCContainer(miniverse) - O = MargPOptimizer(CC_cc) - - try: - # Perform the optimization - r = O.margp_optimizer(src_token) - - # Get the profit in the source token - profit_src = -r.result - - # Get trade instructions in different formats - trade_instructions_df = r.trade_instructions(O.TIF_DFAGGR) - trade_instructions_dic = r.trade_instructions(O.TIF_DICTS) - trade_instructions = r.trade_instructions() - except Exception: - continue - - if trade_instructions_dic is None: - continue - if len(trade_instructions_dic) < 3: - continue - - # Get the candidate ids - cids = [ti["cid"] for ti in trade_instructions_dic] - - # Calculate the profit - profit = self.calculate_profit(src_token, profit_src, self.CCm, cids) - - if str(profit) == "nan": - self.ConfigObj.logger.debug("profit is nan, skipping") - continue - - # Handle candidates based on conditions - candidates += self.handle_candidates( - best_profit, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - # Find the best operations - best_profit, ops = self.find_best_operations( - best_profit, - ops, - profit, - trade_instructions_df, - trade_instructions_dic, - src_token, - trade_instructions, - ) - - return candidates if self.result == self.AO_CANDIDATES else ops diff --git a/fastlane_bot/pool_finder.py b/fastlane_bot/pool_finder.py index f61b5c660..5ebcfbed8 100644 --- a/fastlane_bot/pool_finder.py +++ b/fastlane_bot/pool_finder.py @@ -70,7 +70,7 @@ def get_pools_for_unsupported_pairs(self, config: Config, pools: List[Dict[str, if not carbon_pairs: return [], [], [] self.extract_univ3_fee_tiers(pools) # TODO: these should be configured per exchange - if arb_mode in ["triangle", "multi_triangle"]: + if arb_mode in ["multi_triangle", "multi_triangle_complete", "b3_two_hop"]: 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) diff --git a/fastlane_bot/tests/_data/test_strategies.json b/fastlane_bot/tests/_data/test_strategies.json new file mode 100644 index 000000000..5f377ec25 --- /dev/null +++ b/fastlane_bot/tests/_data/test_strategies.json @@ -0,0 +1,11195 @@ +[ + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 114020327988820, + "z": 1080226227443660, + "A": 0, + "B": 6382340776412 + }, + { + "y": 1879270474439795359, + "z": 1937048429517186009, + "A": 0, + "B": 1875443170982464 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 30784910546, + "B": 6418617024516 + }, + { + "y": 0, + "z": 0, + "A": 88739322630080, + "B": 1876725096051745 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 197764438468, + "B": 6293971818901 + }, + { + "y": 0, + "z": 0, + "A": 235894416821184, + "B": 1873305839476414 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 69065909368, + "B": 6457478834827 + }, + { + "y": 0, + "z": 0, + "A": 106270057837888, + "B": 1874403645842739 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1, + "z": 1, + "A": 0, + "B": 785469658092731 + }, + { + "y": 940344, + "z": 940344, + "A": 0, + "B": 2814748359731 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 45944173154579, + "z": 45944173154579, + "A": 597664135075, + "B": 6374153036317 + }, + { + "y": 62460559661653312295, + "z": 62460559661653312295, + "A": 1091447886596216, + "B": 1834716561142572 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 80181217415, + "B": 6293971818901 + }, + { + "y": 0, + "z": 0, + "A": 164724635005760, + "B": 1875443170982464 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 41578578794, + "B": 6390560149781 + }, + { + "y": 0, + "z": 0, + "A": 88026150692032, + "B": 1878016993404429 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 64327229616, + "B": 5934013538323 + }, + { + "y": 0, + "z": 0, + "A": 75227658897792, + "B": 1893961094774844 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 280078069940593 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 280064065686981 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2843363648269, + "B": 277220702038712 + }, + { + "y": 0, + "z": 0, + "A": 4695920507737, + "B": 274691450305115 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 281193360855543 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 422212465065984 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 166312404377, + "B": 280981963736934 + }, + { + "y": 0, + "z": 0, + "A": 14074804452, + "B": 422212465065984 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 6, + "orders": [ + { + "y": 88000000, + "z": 88000000, + "A": 0, + "B": 281460904017263 + }, + { + "y": 12265324, + "z": 100256523, + "A": 0, + "B": 281460902609959 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 140779742165, + "B": 281432764961555 + }, + { + "y": 0, + "z": 0, + "A": 126755673936, + "B": 281207448346389 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 839262887691, + "B": 280481030188081 + }, + { + "y": 73732931, + "z": 73732931, + "A": 16901673043433, + "B": 264601449757659 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 280994652048914 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 281376512159682 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 281291958475523 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 281334344687532 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 50000000, + "z": 50000000, + "A": 0, + "B": 281334344 + }, + { + "y": 30000000000064988, + "z": 30000000000000000000, + "A": 0, + "B": 5897800738913900 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 6839838, + "B": 274691450 + }, + { + "y": 0, + "z": 0, + "A": 3822814038205952, + "B": 5896589440607106 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 266980, + "B": 281193923 + }, + { + "y": 0, + "z": 0, + "A": 3076021011652608, + "B": 5897666420405006 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2684113799954624, + "B": 4411844567835374 + }, + { + "y": 0, + "z": 0, + "A": 32676704, + "B": 12106696520 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4416648363926356 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12365692607 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2721923874144320, + "B": 4414201427359729 + }, + { + "y": 0, + "z": 0, + "A": 93278858, + "B": 12269209786 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2995657465959328, + "B": 4172302982768969 + }, + { + "y": 0, + "z": 0, + "A": 3368189479, + "B": 8901020307 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2689878730660032, + "B": 4414201427359729 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 10550619334 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2957617717098016, + "B": 4407379944398141 + }, + { + "y": 0, + "z": 0, + "A": 325482628, + "B": 12008132901 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4411937181908343 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12119777774 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 256112904, + "z": 5819227609902409414, + "A": 3239244837475872, + "B": 4405262671506063 + }, + { + "y": 12206514813, + "z": 12206514813, + "A": 336461286, + "B": 11605510599 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 275068659, + "z": 81610723775068476, + "A": 0, + "B": 4414201427359729 + }, + { + "y": 163221447, + "z": 163221447, + "A": 0, + "B": 11605510599 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3323042531979696, + "B": 4401237168483787 + }, + { + "y": 0, + "z": 0, + "A": 847697452, + "B": 11258999068 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 571663139051930, + "z": 571663139051930, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 1000000, + "A": 336461286, + "B": 11605510599 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4414683427557233 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 501939810581681, + "z": 65321473667243437, + "A": 2149178222627328, + "B": 4397502497136945 + }, + { + "y": 154880311, + "z": 154880311, + "A": 149183794, + "B": 13202346667 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4420765724190709 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12132844924 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4420236719061011 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12041077086 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 278221708, + "z": 5427532971167686752, + "A": 0, + "B": 4421567238166879 + }, + { + "y": 10068073661, + "z": 10068073661, + "A": 0, + "B": 12090325050 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4423475823160755 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11975098085 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 305790892, + "z": 305790892, + "A": 0, + "B": 4419191265492220 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12106696520 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 96183253, + "z": 44028673088597604, + "A": 2684113799954624, + "B": 4411844567835374 + }, + { + "y": 89151268, + "z": 89151268, + "A": 162513266, + "B": 12106696520 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 5640649921872484827, + "z": 5640649921872484827, + "A": 0, + "B": 4097479705400261 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12587943637 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4422979815687076 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11895440229 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 172294374, + "z": 101598000200817600, + "A": 2446874816754432, + "B": 4409083376785888 + }, + { + "y": 212739201, + "z": 212739201, + "A": 52100553, + "B": 12139373224 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4416648363926356 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11774943074 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 451650040, + "z": 538817206451648849, + "A": 0, + "B": 4414201427359729 + }, + { + "y": 1077634412, + "z": 1077634412, + "A": 0, + "B": 12139373224 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 261057952059216, + "z": 1050000000006387, + "A": 0, + "B": 4424591350658243 + }, + { + "y": 0, + "z": 1890000, + "A": 0, + "B": 12587943637 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 3927971328867111 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11374514907 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 358952547, + "z": 358952547, + "A": 0, + "B": 4410020295491785 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11639594404 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4418777693966418 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12207709599 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4419191265492220 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11941971885 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 377202152, + "z": 163881956149519, + "A": 0, + "B": 4419191265492220 + }, + { + "y": 311375, + "z": 311375, + "A": 0, + "B": 12139373224 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3041180899054592, + "B": 4409572391052980 + }, + { + "y": 0, + "z": 0, + "A": 169432475, + "B": 11605510599 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2712648399413056, + "B": 4416648363926356 + }, + { + "y": 0, + "z": 0, + "A": 148353165, + "B": 11941971885 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 853464138953307, + "z": 853570940044194, + "A": 1398392653346816, + "B": 4399447365519922 + }, + { + "y": 0, + "z": 2000292, + "A": 10220955, + "B": 13621689681 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 245044388, + "z": 10396749316286936483, + "A": 2702574491156864, + "B": 4419191265492220 + }, + { + "y": 19492172099, + "z": 19492172099, + "A": 164724635, + "B": 11941971885 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 334008425, + "z": 334008425, + "A": 3033037221877376, + "B": 4407379944398141 + }, + { + "y": 0, + "z": 0, + "A": 472515459, + "B": 11081680914 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4426995619646065 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11734502394 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 117569278, + "z": 70390725611403407, + "A": 2788477288474368, + "B": 4423475823160755 + }, + { + "y": 125623289, + "z": 125623289, + "A": 84531001, + "B": 11673578693 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2980772467594688, + "B": 4416648363926356 + }, + { + "y": 0, + "z": 0, + "A": 336461286, + "B": 11605510599 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4417958302722148 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12323976125 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4427463362193231 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11537040912 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 10000000, + "z": 10000000, + "A": 327237901, + "B": 11941971885 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4415169074676328 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12204464163 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3346445279909280, + "B": 4393888993065958 + }, + { + "y": 0, + "z": 0, + "A": 1300589761, + "B": 11258999068 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4419191265492220 + }, + { + "y": 0, + "z": 0, + "A": 1508419854, + "B": 9750579214 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 150705474984807, + "z": 3106922156057720095, + "A": 0, + "B": 4418417946819971 + }, + { + "y": 5971138283, + "z": 5971138283, + "A": 0, + "B": 12236879796 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 451014132, + "z": 220966393433468942, + "A": 0, + "B": 4414441974450480 + }, + { + "y": 440827954, + "z": 440827954, + "A": 0, + "B": 11958546455 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 3000000000000000, + "z": 3000000000000000, + "A": 0, + "B": 3890828006058313 + }, + { + "y": 5000000, + "z": 5000000, + "A": 0, + "B": 744712788 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4418162189973687 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12155678635 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4419191265492220 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12123045882 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 2597621667538172, + "z": 2597621667538172, + "A": 2809096841398592, + "B": 4405262671506063 + }, + { + "y": 0, + "z": 5000000, + "A": 160388634, + "B": 12269209786 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4414201427359729 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12269209786 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4401237168483787 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 13469687901 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4414201427359729 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 10901478971 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 482141129, + "z": 705810482141125, + "A": 0, + "B": 4414201427359729 + }, + { + "y": 1411620, + "z": 1411620, + "A": 0, + "B": 11605510599 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2978302935242656, + "B": 4409572391052980 + }, + { + "y": 0, + "z": 0, + "A": 79933938, + "B": 12349664482 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4421567238166878 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12011431385 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3541231512143552, + "B": 4397465956278860 + }, + { + "y": 0, + "z": 0, + "A": 280058387, + "B": 12024616276 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 115151806292605, + "z": 14941409995240776, + "A": 1602807634633728, + "B": 4412977042158510 + }, + { + "y": 30003964, + "z": 30237200, + "A": 12656044, + "B": 12656037350 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 849465888413110112, + "z": 849465888413110112, + "A": 0, + "B": 4177573949687789 + }, + { + "y": 0, + "z": 3022399625, + "A": 0, + "B": 16789693333 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 40, + "z": 58000000000000000, + "A": 0, + "B": 4415904484669006 + }, + { + "y": 113970000, + "z": 113970000, + "A": 0, + "B": 11858751555 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4394963505536268 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 10995552555 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2246688003806464, + "B": 4421298989777147 + }, + { + "y": 0, + "z": 0, + "A": 66161016, + "B": 11941971885 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2240099232566016, + "B": 4419711917957930 + }, + { + "y": 0, + "z": 0, + "A": 66161016, + "B": 11941971885 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 430884093, + "z": 28675792009831326, + "A": 0, + "B": 4419191265492220 + }, + { + "y": 54484004, + "z": 54484004, + "A": 0, + "B": 12014728964 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2974424587346272, + "B": 4414201427359729 + }, + { + "y": 0, + "z": 0, + "A": 336461286, + "B": 11605510599 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4419711917957930 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4423475823160755 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11908753541 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 4714503053833, + "z": 4714503053833, + "A": 0, + "B": 4411844567835374 + }, + { + "y": 0, + "z": 9570, + "A": 0, + "B": 12682001809 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 90971414, + "z": 59630292195557592, + "A": 0, + "B": 4423047387182259 + }, + { + "y": 108990459, + "z": 108990459, + "A": 0, + "B": 11526735380 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4422596556197854 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11895440229 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 190654501, + "z": 190654501, + "A": 0, + "B": 4405262671506063 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12106696520 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2406242717764096, + "B": 4419347028879599 + }, + { + "y": 0, + "z": 0, + "A": 112165687, + "B": 11598681818 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 15397441, + "z": 50000000000000000, + "A": 2513179298128640, + "B": 4436873015459840 + }, + { + "y": 79246451, + "z": 79246451, + "A": 549851656, + "B": 10531829258 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2406242717763968, + "B": 4419347028879599 + }, + { + "y": 0, + "z": 0, + "A": 6656656, + "B": 11898769952 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 55650722, + "z": 565451074430650722, + "A": 0, + "B": 4436873015459840 + }, + { + "y": 905362672, + "z": 905362672, + "A": 0, + "B": 11199025906 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4397465956278860 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12663246066 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4423752975628488 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1379401469700182, + "z": 1379401469700182, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 3000000, + "A": 150888917, + "B": 13051457750 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 250, + "z": 30000000000000000, + "A": 0, + "B": 4390296313097607 + }, + { + "y": 78270000, + "z": 78270000, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 565308525342914, + "z": 45292446642851149, + "A": 2977322835446848, + "B": 4390587127987123 + }, + { + "y": 112843718, + "z": 112843718, + "A": 57818469, + "B": 13674006857 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 2283582091203080798, + "z": 2283582091203080798, + "A": 0, + "B": 3595009786628585 + }, + { + "y": 0, + "z": 7650000000, + "A": 0, + "B": 16291542113 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3276190001663248, + "B": 4393923342499840 + }, + { + "y": 0, + "z": 0, + "A": 864370565, + "B": 13202346667 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4378798086933123 + }, + { + "y": 0, + "z": 0, + "A": 15685990, + "B": 15144818601 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2759089086748416, + "B": 4212287256575075 + }, + { + "y": 0, + "z": 0, + "A": 490041307, + "B": 15922629181 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2450177769281536, + "B": 4380301238149667 + }, + { + "y": 0, + "z": 0, + "A": 148281031, + "B": 15133673405 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 86728726, + "z": 1000000000000000000, + "A": 2405452822942848, + "B": 4373974675658642 + }, + { + "y": 4174901573, + "z": 4174901573, + "A": 127944264, + "B": 15417019411 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 26700000000000000, + "z": 26700000000000000, + "A": 2477544500919552, + "B": 4214001075359531 + }, + { + "y": 101560000, + "z": 101560000, + "A": 139390008, + "B": 16982064407 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 5820057695805493, + "z": 4850277611940378102, + "A": 0, + "B": 5897921569446869 + }, + { + "y": 40070413, + "z": 40070413, + "A": 0, + "B": 281334204 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 15000000, + "A": 0, + "B": 363382632 + }, + { + "y": 29100000020685479436, + "z": 29100000020685479436, + "A": 0, + "B": 5819312065461623 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 13663355097, + "A": 14444367, + "B": 267030609 + }, + { + "y": 14466218046563547516783, + "z": 14466218046563547516783, + "A": 4727831122555552, + "B": 5860531989906363 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3795565, + "B": 206841020 + }, + { + "y": 0, + "z": 0, + "A": 4697383125322624, + "B": 6071395379309237 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 6082822702171796 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 235498861 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 6063929807463604 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 23617316814, + "A": 13033456, + "B": 267030609 + }, + { + "y": 25020293300078225396153, + "z": 25020293300078225396153, + "A": 0, + "B": 5897934990213120 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 298713785 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 5795656910148557 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 350944, + "z": 350944, + "A": 0, + "B": 178686735 + }, + { + "y": 0, + "z": 870828790345432023, + "A": 0, + "B": 6113776788327848 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 4961497568021296, + "B": 6082527659480557 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 208177340 + }, + { + "y": 1214096088371, + "z": 1214096088371, + "A": 0, + "B": 6089219920240955 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 16084716, + "B": 236350165 + }, + { + "y": 0, + "z": 0, + "A": 4947790025552000, + "B": 6060408646885202 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 17421787206, + "z": 17421787206, + "A": 16794763, + "B": 227474261 + }, + { + "y": 19771348061998657903985, + "z": 42162075649328371866244, + "A": 4934626889212288, + "B": 6056288658238123 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1977160923, + "z": 1977160923, + "A": 0, + "B": 246511664 + }, + { + "y": 748785973789, + "z": 748785973789, + "A": 0, + "B": 6061034492720564 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1977006137, + "z": 1977006137, + "A": 0, + "B": 240985981 + }, + { + "y": 803428059924519968, + "z": 803428059924519968, + "A": 0, + "B": 6064328648548878 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 87945715, + "z": 1979056524, + "A": 0, + "B": 251758872 + }, + { + "y": 2364736489904136220958, + "z": 2364736489904136220958, + "A": 0, + "B": 6058032079864689 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 11726801, + "B": 217805153 + }, + { + "y": 0, + "z": 0, + "A": 4983515678459920, + "B": 6063163109419823 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 6068358068873369 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 2644104685888926, + "z": 2644104685888926, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4421489334575880 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11258999068 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4433658332759707 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11290620597 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 353863996333, + "z": 27846279919032579, + "A": 2974424587346272, + "B": 4414201427359729 + }, + { + "y": 54281685, + "z": 54281685, + "A": 515944006, + "B": 11258999068 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 3731857637334, + "z": 16095736289095175, + "A": 0, + "B": 4418162189973687 + }, + { + "y": 31681205, + "z": 31681205, + "A": 0, + "B": 12171962204 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3373133408542416, + "B": 4409572391052980 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11117370800 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4419711917957930 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12008132901 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4419191265492220 + }, + { + "y": 0, + "z": 0, + "A": 181073997, + "B": 11941971885 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 101639780097, + "z": 1333886763747036, + "A": 0, + "B": 4417350436841658 + }, + { + "y": 2582208, + "z": 2582208, + "A": 0, + "B": 12272438100 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4421309698938599 + }, + { + "y": 0, + "z": 0, + "A": 195373182, + "B": 11398867773 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2684113799954624, + "B": 4411844567835374 + }, + { + "y": 0, + "z": 0, + "A": 167028811, + "B": 11774943074 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 104904690846, + "z": 8925925236828188, + "A": 2709580540260416, + "B": 4421836573316610 + }, + { + "y": 16288091, + "z": 16288091, + "A": 101364381, + "B": 11673578693 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 556237566197240, + "z": 4596907941431047, + "A": 0, + "B": 4406269854284176 + }, + { + "y": 8796054, + "z": 10002871, + "A": 0, + "B": 13127119008 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1629823536669696, + "B": 4404547877816111 + }, + { + "y": 0, + "z": 0, + "A": 15760634, + "B": 13235004853 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3045451058869600, + "B": 4394614688117405 + }, + { + "y": 0, + "z": 0, + "A": 126253607, + "B": 13115042539 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 4407228944666789, + "z": 4407228944666789, + "A": 0, + "B": 0 + }, + { + "y": 1, + "z": 10000000, + "A": 5909117, + "B": 13404830195 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1019869380223632, + "z": 34882751573915346, + "A": 1400739497164800, + "B": 4380278165755879 + }, + { + "y": 100543064, + "z": 103490316, + "A": 0, + "B": 15270141624 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 3635933893360197, + "z": 3636363636363636, + "A": 0, + "B": 0 + }, + { + "y": 1183, + "z": 10000000, + "A": 0, + "B": 14760672305 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 18050036760766174, + "z": 25242207336317756, + "A": 2782651127653696, + "B": 4212287256575075 + }, + { + "y": 47614729, + "z": 47614729, + "A": 119804493, + "B": 16472900673 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 100000267249681, + "z": 168892855794078330, + "A": 3091437773161920, + "B": 4365290227193173 + }, + { + "y": 573034348, + "z": 573394155, + "A": 961648879, + "B": 15922629181 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 10000000000000000, + "z": 10000000000000000, + "A": 3020711672270624, + "B": 4205740810649355 + }, + { + "y": 50000000, + "z": 50000000, + "A": 836467455, + "B": 13789401366 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 18051676019, + "B": 6579823544491 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1875960767845615 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 271063471590013 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 427071836630922 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 271390229048579 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 427224937513865 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 989249281159730240, + "z": 989249281159730240, + "A": 0, + "B": 260275990930613 + }, + { + "y": 422648719819467674, + "z": 1076466230901447812, + "A": 0, + "B": 429081699766967 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 271063471590013 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 427071836630922 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 9468892374457174 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 40847 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 8634428547428518 + }, + { + "y": 0, + "z": 0, + "A": 16826, + "B": 344735 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 8643553693865005 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 349301 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 8658288981030397 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 281474 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 67529602313984, + "B": 1961605126121825 + }, + { + "y": 0, + "z": 0, + "A": 35462792969, + "B": 4450510153742 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 431269233004800, + "B": 1958511393548182 + }, + { + "y": 0, + "z": 0, + "A": 116541993865, + "B": 4360591588697 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1599321369715856 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 3879864801473 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 147979259327424, + "B": 1947513730900029 + }, + { + "y": 0, + "z": 0, + "A": 188537006836, + "B": 4107992041805 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 4305739196507 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 4305739196507 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1936651708354166 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 4875289607221 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 436313179597967670447213, + "z": 436313179597967670447213, + "A": 828844524432536, + "B": 1652392352836564 + }, + { + "y": 0, + "z": 495152571934494392704, + "A": 885362785190, + "B": 5637937459809 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 117043251468756066410185, + "z": 117043251468756066410185, + "A": 528367684077600, + "B": 1937938719334346 + }, + { + "y": 10565345793685714, + "z": 4000000000000000000, + "A": 122211840986, + "B": 4245612257675 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 16600777181628 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1556333024583373 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 19, + "z": 14788612, + "A": 0, + "B": 10956398608834 + }, + { + "y": 9764429049, + "z": 9764429049, + "A": 0, + "B": 1625510896765293 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 16673138657182 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1551238742919704 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 6868466133716, + "B": 9382499223688 + }, + { + "y": 0, + "z": 0, + "A": 1007486702104786, + "B": 1341359303187275 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 606108730652, + "B": 14439361637940 + }, + { + "y": 0, + "z": 0, + "A": 520131008758552, + "B": 1404056791451537 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 462135279054, + "B": 15734929547254 + }, + { + "y": 0, + "z": 0, + "A": 490180602556808, + "B": 1403499920253133 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 17373012734782 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1548301910034222 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 16076674371421 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1560108090863556 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 15045470368592 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1560992044049357 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 8, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 38000000, + "z": 38000000, + "A": 0, + "B": 28147497671065 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 549701052954, + "B": 10754606041864 + }, + { + "y": 0, + "z": 0, + "A": 422256493705712, + "B": 1578842303003821 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 16250965357404 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1551909440538593 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 225954, + "A": 145019191995, + "B": 13123838459100 + }, + { + "y": 105489420, + "z": 105489420, + "A": 67479575262656, + "B": 1589774550646711 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 662134240903, + "B": 13553927787109 + }, + { + "y": 0, + "z": 0, + "A": 270907149576928, + "B": 1580671310638603 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 234748876602, + "B": 13418793005119 + }, + { + "y": 0, + "z": 0, + "A": 103321412857472, + "B": 1585482192518094 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 159943, + "z": 159943, + "A": 60276712435, + "B": 10578477411337 + }, + { + "y": 115947227, + "z": 115947227, + "A": 54250689295040, + "B": 1634716029499553 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 25152288529636, + "B": 449688449198810 + }, + { + "y": 0, + "z": 0, + "A": 18996714668468, + "B": 199032864766430 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 34703988685704, + "B": 431501643387882 + }, + { + "y": 0, + "z": 0, + "A": 27237657162642, + "B": 235498861483415 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 44776582305246, + "B": 480507841477086 + }, + { + "y": 0, + "z": 0, + "A": 23850212032037, + "B": 154170194117667 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 198833981052026, + "B": 199032864766430 + }, + { + "y": 0, + "z": 0, + "A": 198833981052026, + "B": 199032864766430 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 429825315168743 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 235498861483415 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 20667668420221, + "B": 238839437719716 + }, + { + "y": 0, + "z": 0, + "A": 11083683461932, + "B": 425114574273757 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 6905566947379453929203, + "z": 11563446583847796692757, + "A": 125354052836, + "B": 439452415580873 + }, + { + "y": 3694388464380823259863, + "z": 3694388464380823259863, + "A": 13683045595446, + "B": 222525507687130 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 24396561696931145739518, + "z": 80260419904582748806760, + "A": 17440186822628, + "B": 423641308686677 + }, + { + "y": 54619678868177246632846, + "z": 57749208314629338542747, + "A": 15893791798581, + "B": 166522841916634 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 32707572641770, + "B": 463166292906404 + }, + { + "y": 0, + "z": 0, + "A": 21012458616726, + "B": 178020406149704 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 730266422975475261938, + "z": 730266422975475261938, + "A": 33223614234442, + "B": 422212465065984 + }, + { + "y": 2000000000000000000000, + "z": 2000000000000000000000, + "A": 48911975044060, + "B": 178020406149704 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 192934254200685368, + "z": 192934254200685368, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 32103238, + "B": 132023466 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 214539289391527325, + "z": 1214539289391527325, + "A": 4955113887665360, + "B": 6118076961470784 + }, + { + "y": 395911, + "z": 395911, + "A": 7087840, + "B": 164126704 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4721127277721440, + "B": 6077451113620241 + }, + { + "y": 0, + "z": 0, + "A": 7457603, + "B": 208747429 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 6077451113620241 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 206841020 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 8972371843881888025358, + "z": 19316685232156394881608, + "A": 0, + "B": 0 + }, + { + "y": 4711272311, + "z": 10000000000, + "A": 52376792, + "B": 178020406 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 202580033361367509166, + "z": 202580033361367509166, + "A": 54951968265652, + "B": 422212465065984 + }, + { + "y": 12760538178735827538, + "z": 100000000000000000000, + "A": 74029518581747, + "B": 219838984563023 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1564682925467016, + "B": 2410500647253646 + }, + { + "y": 0, + "z": 0, + "A": 58705469968, + "B": 1320234666636 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 824421119442, + "B": 1990328647664 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4944431896972025 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 2686575069 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 7144674823524943676279644, + "z": 7144674823524943676279644, + "A": 0, + "B": 3313534809917929 + }, + { + "y": 0, + "z": 2500000000000000000, + "A": 0, + "B": 166522841916 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1315789473685020981743419, + "z": 1315789473685020981743419, + "A": 0, + "B": 3301106638963764 + }, + { + "y": 0, + "z": 500000000000000000, + "A": 0, + "B": 173512828792 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1165907528222, + "B": 2814749767106 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5334985036791808 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 6409950565275210 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 870136136568513756, + "A": 4203375527547488, + "B": 5535851913891364 + }, + { + "y": 298837714, + "z": 298837714, + "A": 20981930, + "B": 744712788 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4099799296787712, + "B": 5524546625995471 + }, + { + "y": 0, + "z": 0, + "A": 7645157, + "B": 773416205 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 237941278490380013, + "z": 237941278490380013, + "A": 0, + "B": 5494142312184980 + }, + { + "y": 236053, + "z": 236053, + "A": 42444919, + "B": 912083168 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 10000000000000000000, + "z": 10000000000000000000, + "A": 4107354125333056, + "B": 5290440228071742 + }, + { + "y": 0, + "z": 219772609, + "A": 64597175, + "B": 1194197188 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4431814251932800, + "B": 5537837088750967 + }, + { + "y": 0, + "z": 0, + "A": 55242720, + "B": 689470068 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4197273666249056, + "B": 5546543421420288 + }, + { + "y": 0, + "z": 0, + "A": 27993069, + "B": 701206927 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 2761587701107309601, + "z": 2761587701107309601, + "A": 0, + "B": 5553443463810064 + }, + { + "y": 0, + "z": 13416, + "A": 0, + "B": 663442882 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5550942688830526 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 717623199 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5553022091250847 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 721968081 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4405324597851168, + "B": 5549976527597997 + }, + { + "y": 0, + "z": 0, + "A": 8119874, + "B": 692943491 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4421494055940880, + "B": 5549976527597997 + }, + { + "y": 0, + "z": 0, + "A": 5309989, + "B": 692943491 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5557954127867376 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 718431890 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 352000000000000000, + "z": 352000000000000000, + "A": 0, + "B": 5550942688830527 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 732916735 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 150000000000000000, + "z": 150000000000000000, + "A": 4462918186838720, + "B": 5541596117654575 + }, + { + "y": 0, + "z": 0, + "A": 49209092, + "B": 663442882 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5549519298835160 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 664181272 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5562168090782685 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 705037227 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5560241418928941 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 703687441 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 200013628799990774, + "z": 200013628799990774, + "A": 0, + "B": 5561512790973167 + }, + { + "y": 0, + "z": 8202, + "A": 0, + "B": 704193914 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5561426524297455 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 703687441 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5572613773242110 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 631866284 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4383723320293632, + "B": 5579572136367413 + }, + { + "y": 0, + "z": 0, + "A": 29855577, + "B": 590292689 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2485288710369280, + "B": 5575870031536780 + }, + { + "y": 0, + "z": 0, + "A": 331536, + "B": 662574878 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 663, + "z": 6480, + "A": 653177189682, + "B": 15734929547254 + }, + { + "y": 1780000, + "z": 1780000, + "A": 168240307215872, + "B": 1551909440538593 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 17209238917955 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1549188527645502 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 48118576872, + "B": 11126275384356 + }, + { + "y": 0, + "z": 0, + "A": 11223145838304, + "B": 1627804342886535 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1526170, + "z": 1532578, + "A": 0, + "B": 0 + }, + { + "y": 4165461, + "z": 1000000000, + "A": 27548016818688, + "B": 1631632133371359 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 288551667147, + "z": 359732340414, + "A": 119127330708, + "B": 2267453933449 + }, + { + "y": 1000000000000000, + "z": 1000000000000000, + "A": 1002503361642560, + "B": 2210109140007279 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1619339587886900, + "B": 2163191935067045 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 6667651067822137 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 39806572 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5619531847891604 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 515183796 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5613166202183792 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 511324687 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4155481966138848, + "B": 5613166202183792 + }, + { + "y": 0, + "z": 0, + "A": 10938555, + "B": 537757187 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 9998214320035449176736, + "z": 9998214320035449176736, + "A": 4735925375135328, + "B": 5897934990213120 + }, + { + "y": 1607129, + "z": 9000000000, + "A": 0, + "B": 267030609 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5232231541229414 + }, + { + "y": 0, + "z": 0, + "A": 46278427, + "B": 1688849860 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 11537531850305, + "B": 267164224749167 + }, + { + "y": 1000000000000000000, + "z": 1000000000000000000, + "A": 4298139552137, + "B": 274347871120865 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 554622286736056, + "B": 1277805245355055 + }, + { + "y": 0, + "z": 0, + "A": 1331251905142, + "B": 24598078728255 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 257521366189072, + "B": 1333225807640988 + }, + { + "y": 0, + "z": 0, + "A": 2034139056268, + "B": 18457548559094 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 162771525248320, + "B": 1327696120430981 + }, + { + "y": 0, + "z": 0, + "A": 1533209099423, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 482257633450896, + "B": 1276723935194254 + }, + { + "y": 0, + "z": 0, + "A": 1325777918934, + "B": 24376448036106 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 162771525248320, + "B": 1327696120430981 + }, + { + "y": 0, + "z": 0, + "A": 1347608241728, + "B": 19903286476643 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 2457499603880843 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 431441270486960914582, + "z": 431441270486960914582, + "A": 0, + "B": 6079405896095884 + }, + { + "y": 5429417, + "z": 260837212, + "A": 0, + "B": 218029579 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 59237394628, + "z": 59237394628, + "A": 0, + "B": 343876415049 + }, + { + "y": 154513348249492, + "z": 454513348249491, + "A": 0, + "B": 2442240813967327 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2732534316972, + "B": 277345535623620 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 422212465065984 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 280346815951510 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 276008922853021 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1594908216899813 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11537040912814 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 395803389286127, + "z": 395803389286127, + "A": 1652773131753472, + "B": 4413961780632971 + }, + { + "y": 0, + "z": 0, + "A": 95247358, + "B": 12429598420 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 991147447490306, + "z": 991147447490306, + "A": 3841541776056576, + "B": 5548903258865350 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 110403569141208 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 703687441776640 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 456188789675127186, + "z": 18157173413575952127, + "A": 1485563670673, + "B": 113449103612417 + }, + { + "y": 107518213723408094365, + "z": 107518213723408094365, + "A": 8319388976480, + "B": 729163250930037 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1653233157729138 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 10050667003024 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 15089931911881285789523, + "z": 15089931911881285789523, + "A": 0, + "B": 2401424394480145 + }, + { + "y": 7437560995464045, + "z": 7437560995464045, + "A": 0, + "B": 1462586882166 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 280078069940593 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 280064065686981 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 50862549210987139697776760, + "z": 50863142200792615297776760, + "A": 0, + "B": 4112448365789184 + }, + { + "y": 5929898054756, + "z": 5929898054756, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3626639273868288, + "B": 5895551073702265 + }, + { + "y": 0, + "z": 0, + "A": 1641510, + "B": 280614882 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 205388472487596, + "z": 205388472487596, + "A": 0, + "B": 3039186638298905 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 437372905685264, + "B": 1374690987800661 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 16169506371471 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 406090105314091472, + "z": 406090105314091472, + "A": 437372905685264, + "B": 1374690987800661 + }, + { + "y": 8659902652463658, + "z": 10000000000000000, + "A": 0, + "B": 16169506371471 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 469019352141152, + "B": 1344104119528935 + }, + { + "y": 0, + "z": 0, + "A": 29072992434, + "B": 19061488748007 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 71173815618929296314, + "z": 71173815618929296314, + "A": 0, + "B": 1316713955474930 + }, + { + "y": 3156221636398, + "z": 400000000000000000, + "A": 0, + "B": 21101238668148 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1399247190132272 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 17102934719008 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 11119662563124556402914, + "z": 11119662563124556402914, + "A": 0, + "B": 5819312065461623 + }, + { + "y": 2621463, + "z": 3333692120, + "A": 0, + "B": 154170194 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3857526005628672, + "B": 5885442699136201 + }, + { + "y": 0, + "z": 0, + "A": 2917414, + "B": 290478902 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 265000531007726 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 430922020886079 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4652986612949312, + "B": 6056554076835875 + }, + { + "y": 0, + "z": 0, + "A": 4958899, + "B": 237175031 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4429715524779008, + "B": 6068496639805532 + }, + { + "y": 0, + "z": 0, + "A": 347793, + "B": 227629562 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4112383408345024, + "B": 5910599450426869 + }, + { + "y": 0, + "z": 0, + "A": 2540334, + "B": 266118988 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1200000000000000000000, + "z": 1200000000000000000000, + "A": 87866796034631, + "B": 116876122538783 + }, + { + "y": 0, + "z": 0, + "A": 100594940936255, + "B": 237175031116531 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 6623984446141876 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 13499065663326 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 281053711854908 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 281052447110359 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 47577954833324 + }, + { + "y": 1320938095174909, + "z": 1320938095174909, + "A": 0, + "B": 733875039137642 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 66011733 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2799743397609152, + "B": 4403216372410434 + }, + { + "y": 0, + "z": 0, + "A": 310860233, + "B": 12587943637 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 58232690379, + "z": 58232690379, + "A": 0, + "B": 336426944976 + }, + { + "y": 39810447164362, + "z": 34979424674564094, + "A": 0, + "B": 3027669278273452 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 990109346452631762545, + "z": 1000000000000000000000, + "A": 0, + "B": 2125819262573365 + }, + { + "y": 3103130709473749, + "z": 3278130709473676, + "A": 0, + "B": 1665228419166 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 272750143645176 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 130090536899460562046, + "z": 130090536899460562046, + "A": 0, + "B": 1032493309739369 + }, + { + "y": 8188211867643, + "z": 2000000000000000, + "A": 0, + "B": 41749485928737 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1006767584419692866381, + "z": 1006767584419692866381, + "A": 0, + "B": 1617222447892624 + }, + { + "y": 9063346216006510, + "z": 9063346216006510, + "A": 0, + "B": 9151183954963 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1967006744872849 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 300059704431121453727476379, + "z": 300059704431121453727476379, + "A": 0, + "B": 9182351582240718 + }, + { + "y": 3993424, + "z": 40000000, + "A": 0, + "B": 97505 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 6698879436048063410, + "z": 6698879436048063410, + "A": 4431814251932800, + "B": 5537837088750967 + }, + { + "y": 1455599, + "z": 40000000, + "A": 29352735, + "B": 660117333 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5852013881602074, + "B": 6072553667768913 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 89010203 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 6297921030494744, + "z": 6297921030494744, + "A": 4732262456775128, + "B": 5519521679555936 + }, + { + "y": 0, + "z": 0, + "A": 111899136, + "B": 616680776 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 24496793394094885485507116831, + "z": 24496793394094885485507116831, + "A": 0, + "B": 9733798425685482 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 7001772345365, + "B": 143452931340556 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 514861698116473 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 141260015913187752410804, + "z": 1809224000000000000000000, + "A": 6752374180268429, + "B": 6667651067822137 + }, + { + "y": 18536527908, + "z": 22158378151, + "A": 28605233, + "B": 19903286 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 4796297636542810062982, + "z": 4796297636542810062982, + "A": 0, + "B": 6179409966923776 + }, + { + "y": 0, + "z": 1199074409, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 526169922884464, + "B": 1344104119528935 + }, + { + "y": 0, + "z": 0, + "A": 221151841098, + "B": 17802040614970 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 389260596992764065626, + "z": 389260596992764065626, + "A": 73896121829251, + "B": 127157432964074 + }, + { + "y": 6894886662123410454, + "z": 48147078620508577564, + "A": 91933152437035, + "B": 272900114993395 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4413923443814984, + "B": 5316189484112771 + }, + { + "y": 0, + "z": 0, + "A": 175033761, + "B": 867564143 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4373901033704560, + "B": 5490999627528876 + }, + { + "y": 0, + "z": 0, + "A": 116551602, + "B": 689470068 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4648840574297940, + "B": 5316189484112771 + }, + { + "y": 0, + "z": 0, + "A": 59941261, + "B": 796131459 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 2800904404, + "z": 1369131800779269345736, + "A": 4176670902969136, + "B": 5273153034810362 + }, + { + "y": 36648168274, + "z": 36648168274, + "A": 131895556, + "B": 1198501809 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 331891386660038816, + "z": 1387281614660444091484, + "A": 4173664385879456, + "B": 5273153034810362 + }, + { + "y": 48268772131, + "z": 48268772131, + "A": 131895556, + "B": 1198501809 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 42390882284056, + "B": 804312754715802 + }, + { + "y": 0, + "z": 0, + "A": 8822601752766, + "B": 62939718189019 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 21913926715434, + "B": 97505792144424 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4706710275272784, + "B": 5832417665541183 + }, + { + "y": 0, + "z": 0, + "A": 12590463, + "B": 308340388 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5856368901971509 + }, + { + "y": 44883, + "z": 44883, + "A": 0, + "B": 281474976 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5008298865623888, + "B": 5862262594284473 + }, + { + "y": 0, + "z": 0, + "A": 33910701, + "B": 282878850 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 258014379556, + "z": 258014379556, + "A": 0, + "B": 5623071857730359 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 451238714 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 93861239118, + "B": 1641267048802 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1406040702125280, + "B": 2171067277231971 + }, + { + "y": 0, + "z": 0, + "A": 830087488546, + "B": 1780204061497 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 124776500000000000000, + "z": 124776500000000000000, + "A": 4667141748127616, + "B": 5869595505087981 + }, + { + "y": 27266040, + "z": 150000000, + "A": 8180665, + "B": 274347871 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5244205702679168, + "B": 6632101573718840 + }, + { + "y": 0, + "z": 0, + "A": 699343, + "B": 56294995 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 140310875172183300743, + "z": 140310875172183300743, + "A": 0, + "B": 1551981762062751 + }, + { + "y": 2265082622717299, + "z": 436830434431163669, + "A": 0, + "B": 15671863443580 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 16924582675920 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 27587206617950852848, + "z": 27587206617950852848, + "A": 0, + "B": 1107405274288315 + }, + { + "y": 0, + "z": 160663559791647, + "A": 0, + "B": 32461277877491 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 32216283599087 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 500000000000000000000, + "z": 500000000000000000000, + "A": 0, + "B": 5897934990213120 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 716000000000000000000, + "z": 716000000000000000000, + "A": 0, + "B": 5897934990213120 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 8484634085413606890074, + "z": 10000000000000000000000, + "A": 6076876746517352, + "B": 5897934990213120 + }, + { + "y": 330781913, + "z": 330781913, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1848764866204, + "B": 268375860102384 + }, + { + "y": 0, + "z": 0, + "A": 4790187582350, + "B": 424999616594226 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5016805384016, + "B": 268375860102384 + }, + { + "y": 0, + "z": 0, + "A": 4170429127142, + "B": 422914402030655 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 421071667761, + "B": 274635098862131 + }, + { + "y": 0, + "z": 0, + "A": 441188629542, + "B": 425352957785920 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1174124187331, + "B": 273407701669518 + }, + { + "y": 0, + "z": 0, + "A": 1237567498278, + "B": 425565563130911 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 6222958763021685152, + "z": 6222958763021685152, + "A": 0, + "B": 271590536103119 + }, + { + "y": 0, + "z": 6684166233105999089, + "A": 0, + "B": 425189445062038 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 425122056087318 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 199032864766430 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 104053708281024, + "B": 802905379445375 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 65105350736426 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1611935186395326 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11778306863147 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 278117629226340 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 423840206370747 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 87899750790550602, + "z": 87899750790550602, + "A": 0, + "B": 0 + }, + { + "y": 9955495290160026, + "z": 100000000000000000, + "A": 0, + "B": 423919114603035 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 628422000000000000000001, + "z": 628422000000000000000001, + "A": 718858611636217, + "B": 188913635010583 + }, + { + "y": 31409372044597691255, + "z": 31409372044597691255, + "A": 137384694020, + "B": 2814749767106 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 9471423612478089182941, + "z": 9471423612478089182941, + "A": 4734044203611648, + "B": 6911138420176138 + }, + { + "y": 2450761, + "z": 60000000, + "A": 369963, + "B": 27440562 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 164126704880 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 446464641988277417053803446959, + "z": 446464641988277417053803446959, + "A": 4983852699256107, + "B": 4452607563567569 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 6183526041137572, + "B": 6365723829404364 + }, + { + "y": 0, + "z": 0, + "A": 20433334, + "B": 36699846 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1345959829710589151063446, + "z": 1345959829710589151063446, + "A": 3077996374342343, + "B": 1874950655595335 + }, + { + "y": 655299899142686, + "z": 17956091241899154, + "A": 0, + "B": 62939718186 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 25000000000000000000000, + "z": 25000000000000000000000, + "A": 1853599217686804, + "B": 1864771720708096 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1194197188599, + "B": 796131459065 + }, + { + "y": 0, + "z": 0, + "A": 1955758048604772, + "B": 1885536479604622 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 24443372516216376738, + "z": 24443372516216376738, + "A": 0, + "B": 6421702156646 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 53327255824 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 3824708776862184 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 5789920402598581 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 462510582 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 10, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 2144866896817738 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 10, + "tkn1_decimals": 18, + "orders": [ + { + "y": 52650000000000, + "z": 52650000000000, + "A": 0, + "B": 1990328647664 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 181824139988833051288942, + "z": 186145292469076000000000, + "A": 0, + "B": 2738986923689801 + }, + { + "y": 30861671013895208, + "z": 30861671013895208, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1065887633596146948, + "z": 48609684833595929325, + "A": 0, + "B": 5799273023354302 + }, + { + "y": 118859493, + "z": 118859493, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 10000000000000000000000, + "z": 10000000000000000000000, + "A": 6076876746517352, + "B": 5897934990213120 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 6917531720982944, + "B": 8337536991275690 + }, + { + "y": 0, + "z": 0, + "A": 14790, + "B": 796131 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 756336231240 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 2696228942315501 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 40007098349, + "B": 807516960869 + }, + { + "y": 0, + "z": 0, + "A": 1381252234741504, + "B": 2531668182481780 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1012897976476896, + "B": 1951683411026676 + }, + { + "y": 0, + "z": 0, + "A": 189819445889, + "B": 4078959932139 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 727748195705208, + "B": 1556055992407418 + }, + { + "y": 0, + "z": 0, + "A": 574683172206, + "B": 13499065663326 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 147012799967808, + "B": 1570714230392852 + }, + { + "y": 0, + "z": 0, + "A": 574683172206, + "B": 13499065663326 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 218026135828320, + "B": 1556055992407418 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 10872151009174382983809604644454, + "z": 10872151009174382983809604644454, + "A": 0, + "B": 1277562318686195 + }, + { + "y": 1887959073671158, + "z": 1887959073671158, + "A": 0, + "B": 32649762691529 + } + ], + "pm_within_range": true, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 70779239735011, + "z": 70779239735011, + "A": 0, + "B": 6123191372350253 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 154170194 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 512000000000000000000, + "z": 512000000000000000000, + "A": 0, + "B": 1836272714261246 + }, + { + "y": 640000000075723, + "z": 640000000075723, + "A": 0, + "B": 6418617024516 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 2326356551626507395, + "z": 383881236174425029579, + "A": 0, + "B": 273650948647911 + }, + { + "y": 977334249787509956689, + "z": 977334249787509956689, + "A": 0, + "B": 424861545720628 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 6538780053, + "z": 415385877386, + "A": 0, + "B": 1849443812469839 + }, + { + "y": 310181613, + "z": 310181613, + "A": 0, + "B": 7176231994178 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 2829551, + "z": 2829551, + "A": 39455875, + "B": 281474976 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 37832237, + "B": 295213446 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 109062342843, + "B": 562949953421 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1881451084024880, + "B": 2961678188674842 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 422650035874176, + "B": 2488377048315128 + }, + { + "y": 0, + "z": 0, + "A": 6293991488, + "B": 1255643434273 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 2507431074685914 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 1160551059946 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 2178765401186913 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 3076835567987795 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 288426022820 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 3069967496543391 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 295213446112 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 3083003136545149 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 281474976710 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 4932993, + "B": 34473503 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 524895183942648, + "B": 1322586526183310 + }, + { + "y": 0, + "z": 0, + "A": 1079874841735, + "B": 17802040614970 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1111001100000000000000, + "z": 1111001100000000000000, + "A": 5784860515563795, + "B": 5588120528377325 + }, + { + "y": 10906070, + "z": 10906070, + "A": 26070485, + "B": 62939718 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1287311221247920, + "B": 1868399357217812 + }, + { + "y": 0, + "z": 0, + "A": 229951284662, + "B": 3330456838332 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4646972935680384, + "B": 6056554076835875 + }, + { + "y": 0, + "z": 0, + "A": 8265614, + "B": 235498860 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 917889101643, + "A": 0, + "B": 6091136592652313 + }, + { + "y": 1, + "z": 1, + "A": 0, + "B": 207986962 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4733645512029072, + "B": 5880912373628182 + }, + { + "y": 0, + "z": 0, + "A": 15842147, + "B": 242133930 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 164946645907471646633662, + "z": 164946645907471646633662, + "A": 4714672617918272, + "B": 5908727473778234 + }, + { + "y": 573330, + "z": 142123920000, + "A": 12146907, + "B": 257105820 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 195684245010154624532, + "z": 910465506678778254786, + "A": 5253657835653328, + "B": 6156021435066085 + }, + { + "y": 191816035, + "z": 191816035, + "A": 6829721, + "B": 112589990 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4961233126255984, + "B": 5784480816994759 + }, + { + "y": 0, + "z": 0, + "A": 32413840, + "B": 350433519 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4740513264175648, + "B": 5804981281429040 + }, + { + "y": 0, + "z": 0, + "A": 33546099, + "B": 349301260 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1893569036865084, + "B": 1923935334773188 + }, + { + "y": 0, + "z": 0, + "A": 231022833679, + "B": 2302251956716 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 6958207478080252, + "B": 7518304992073213 + }, + { + "y": 0, + "z": 0, + "A": 1034312, + "B": 3504335 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2701954172481160, + "B": 3268698482233455 + }, + { + "y": 0, + "z": 0, + "A": 20602064997, + "B": 143524639883 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5876154451360166, + "B": 6123191372350253 + }, + { + "y": 0, + "z": 0, + "A": 8132558, + "B": 93354688 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 26557508752630579812, + "z": 7216878756175068527045, + "A": 6157268630804904, + "B": 6747895254374806 + }, + { + "y": 87990319, + "z": 87990319, + "A": 3372930, + "B": 21802957 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 192342115313, + "B": 6634428825547 + }, + { + "y": 0, + "z": 0, + "A": 213123355203392, + "B": 1861441668283043 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 789000000, + "z": 789000000, + "A": 0, + "B": 7898378310 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 759652414358893387545, + "z": 759652414358893387545, + "A": 534630153766112, + "B": 1923773774689185 + }, + { + "y": 29609344, + "z": 234471700757066867, + "A": 194911757977, + "B": 4848632445673 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 841196995683970014742, + "z": 841196995683970014742, + "A": 526499337013760, + "B": 1913345201398967 + }, + { + "y": 0, + "z": 285548746573665377, + "A": 111291379285, + "B": 5132006475352 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 131588900000000, + "z": 131588900000000, + "A": 0, + "B": 8058605140777 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 27757261810280396, + "z": 27757261810280396, + "A": 0, + "B": 145353052956598 + }, + { + "y": 73857482991097, + "z": 100000000000000000, + "A": 0, + "B": 548505585935212 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3586561961500480, + "B": 4414201427359729 + }, + { + "y": 0, + "z": 0, + "A": 1306329913, + "B": 8444249301 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 150518014667394, + "B": 463166292906404 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1091044687305312, + "B": 2144172889855162 + }, + { + "y": 0, + "z": 0, + "A": 413097790108, + "B": 2670306092245 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3008452723492480, + "B": 4115990979568204 + }, + { + "y": 0, + "z": 0, + "A": 751294024, + "B": 15442693066 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 17805604, + "B": 436059158 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 922496429512409284839, + "z": 922496429512409284839, + "A": 1613640030779330, + "B": 1851074249724425 + }, + { + "y": 0, + "z": 414512558735910396, + "A": 734680726150, + "B": 3093792868526 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 4, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 6248455, + "A": 19983057895712, + "B": 58691589840551 + }, + { + "y": 59557321, + "z": 59557321, + "A": 163388096968052, + "B": 760734950934819 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 848793802027334, + "z": 848793802027334, + "A": 5180294995126, + "B": 17802040614970 + }, + { + "y": 10000000000000000, + "z": 10000000000000000, + "A": 0, + "B": 1116961904425590 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1833929899998736, + "B": 2707122843276474 + }, + { + "y": 0, + "z": 0, + "A": 106661390763, + "B": 689470068302 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5288702606555848, + "B": 6065852576056416 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 6143562758308004 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1927475580338043, + "B": 1908752185819135 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1836413407069254, + "B": 1942771193973374 + }, + { + "y": 0, + "z": 0, + "A": 961805813462, + "B": 1990328647664 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 435077384453232, + "B": 1604061502893966 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2695292964, + "B": 36338263239 + }, + { + "y": 0, + "z": 0, + "A": 2980679241121640, + "B": 3876484763339241 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 9789785974, + "B": 6046587420550 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 59042689222 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1604061502893966 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 12269209786172 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 1609171097141637 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 11941971885985 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 80622013121218626866698, + "z": 80952380952380952380952, + "A": 0, + "B": 0 + }, + { + "y": 693772446189188, + "z": 170000000000000000, + "A": 0, + "B": 407895993213 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 933975987204825869086189, + "z": 933975987204825869086189, + "A": 6933922936336988, + "B": 7188768194474047 + }, + { + "y": 2878100, + "z": 121323366, + "A": 793046, + "B": 8629858 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 3575080451162048, + "B": 4776330323397328 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 3828473594 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 4929204269463982 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 4107992041 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 79044559783108, + "B": 761445498018487 + }, + { + "y": 0, + "z": 0, + "A": 8816954911021, + "B": 88564033833787 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 92735026918963095814, + "z": 92735026918963095814, + "A": 151273942179264, + "B": 720299248893861 + }, + { + "y": 1, + "z": 2000000000000000000, + "A": 7542099269919, + "B": 48752896072212 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 5498705775777494197683, + "z": 5498705775777494197683, + "A": 0, + "B": 1942771193973374 + }, + { + "y": 676313484272643485, + "z": 676313484272643485, + "A": 0, + "B": 89010203 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 26954073820629498268, + "z": 26954073820629498268, + "A": 1019123633620960, + "B": 2486216480368414 + }, + { + "y": 0, + "z": 0, + "A": 31086023232, + "B": 1258794363780 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 709402529327632, + "B": 1876383193610469 + }, + { + "y": 0, + "z": 0, + "A": 322985812970, + "B": 5970985942992 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 2373617178370598938928, + "z": 2439024390243902439024, + "A": 0, + "B": 0 + }, + { + "y": 5363391373649041, + "z": 200000000000000000, + "A": 0, + "B": 2548864320863 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 65635826127214348854402, + "z": 65703622720216780897407, + "A": 15654270644224, + "B": 2496183887650708 + }, + { + "y": 1370968357848322, + "z": 1328976863908992711, + "A": 316715624, + "B": 1265754606252 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 15178601618062464231984, + "z": 17316698433060774976850, + "A": 1875316236673672, + "B": 2144172889855162 + }, + { + "y": 154848284976097849, + "z": 1803480794187027030, + "A": 1233655536178, + "B": 2300346228840 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 16648574541264, + "B": 1016107086572665 + }, + { + "y": 0, + "z": 0, + "A": 918853223949, + "B": 55586854003948 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 265692101501361 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5691440146330, + "B": 93824992236885 + }, + { + "y": 0, + "z": 0, + "A": 89121955491300, + "B": 774056185954304 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 543867505301, + "B": 93877160710327 + }, + { + "y": 0, + "z": 0, + "A": 4833970773032, + "B": 771548300769681 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 2267879961346843022297, + "z": 2267879961346843022297, + "A": 19977343161047, + "B": 89010203074852 + }, + { + "y": 152396102373804984538, + "z": 10000000000000000000000, + "A": 117477996738520, + "B": 744686686769674 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 19983591358672, + "B": 512845982325809 + }, + { + "y": 0, + "z": 0, + "A": 9956510762654, + "B": 154170194117592 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 61945144797004, + "B": 785475460995752 + }, + { + "y": 0, + "z": 0, + "A": 7586632351644, + "B": 73725574260528 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 43950709191539, + "z": 43950709191539, + "A": 5527220689819904, + "B": 6899262881387257 + }, + { + "y": 0, + "z": 0, + "A": 1948515, + "B": 29521344 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 4942033433454720, + "B": 6736743059419188 + }, + { + "y": 0, + "z": 0, + "A": 1750981, + "B": 33547981 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2144960201048042, + "B": 2393746154463176 + }, + { + "y": 0, + "z": 0, + "A": 16943247471, + "B": 1160551059946 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 989374721, + "B": 326108152098 + }, + { + "y": 408549062848342, + "z": 408549062848342, + "A": 731572590172416, + "B": 3009428539562415 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 2, + "orders": [ + { + "y": 98229741454066541, + "z": 98229741454066541, + "A": 6730402528131024, + "B": 7517178472700041 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1994817347236009497149, + "z": 4382303839732888146911, + "A": 0, + "B": 0 + }, + { + "y": 7150522045028888078, + "z": 13125000000000000000, + "A": 0, + "B": 15404166537992 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5056948365070752, + "B": 6437772386901106 + }, + { + "y": 0, + "z": 0, + "A": 6392845, + "B": 65673901 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1687222920268688, + "B": 2716526728358570 + }, + { + "y": 0, + "z": 0, + "A": 78621420998, + "B": 666091367666 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2220870919587560, + "B": 2716435102389589 + }, + { + "y": 0, + "z": 0, + "A": 215239039251, + "B": 629397181890 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 14719499307277068, + "z": 14719499307277068, + "A": 0, + "B": 51475929535534 + }, + { + "y": 2298321674211393568940, + "z": 2298321674211393568940, + "A": 0, + "B": 1019641690101662 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1000000000000000000000000, + "z": 1000000000000000000000000, + "A": 1046453546274596, + "B": 422212465065984 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 100000000000000000000000000, + "z": 100000000000000000000000000, + "A": 0, + "B": 4162440831414697 + }, + { + "y": 0, + "z": 599999999999999999, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 334273675242777444258, + "z": 334273675242777444258, + "A": 0, + "B": 4931531773285292 + }, + { + "y": 0, + "z": 71868840177, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 10000000000000000000000, + "z": 10000000000000000000000, + "A": 5266057141120072, + "B": 5849410428367404 + }, + { + "y": 0, + "z": 12145369487, + "A": 35849174, + "B": 225179981 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 1, + "z": 1, + "A": 0, + "B": 1338146848319137 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 13202346667595 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 780422831284795, + "z": 780422831284795, + "A": 0, + "B": 0 + }, + { + "y": 299999102514332704086383, + "z": 300000000000000000000000, + "A": 1106029368249440, + "B": 1640263801218312 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 3000000000000000000000, + "A": 0, + "B": 0 + }, + { + "y": 300000000000000000000000, + "z": 300000000000000000000000, + "A": 1657716079701301, + "B": 785475461108442 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 140845070422535211267, + "A": 0, + "B": 0 + }, + { + "y": 100000000000000000000000, + "z": 100000000000000000000000, + "A": 0, + "B": 1641754040568763 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 6, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 810254654440, + "A": 0, + "B": 0 + }, + { + "y": 250000000000000000000000, + "z": 250000000000000000000000, + "A": 4735291325787168, + "B": 5771542226142764 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 3000000000000000000000, + "z": 3000000000000000000000, + "A": 5318099449305272, + "B": 5785788699108529 + }, + { + "y": 0, + "z": 6310705190, + "A": 75285497, + "B": 251758872 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 3007111112051022, + "A": 0, + "B": 5793779365402324 + }, + { + "y": 6766, + "z": 6766, + "A": 0, + "B": 422212465 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 66545871488, + "B": 4691249611844 + }, + { + "y": 0, + "z": 0, + "A": 122963810179840, + "B": 1939577087784125 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 8985740742937015 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 10000000000000000000000000000, + "z": 10000000000000000000000000000, + "A": 0, + "B": 1012714354797197 + }, + { + "y": 0, + "z": 437105185185185200000000000, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 8, + "orders": [ + { + "y": 0, + "z": 0, + "A": 5608800189659048, + "B": 6468615313807127 + }, + { + "y": 0, + "z": 0, + "A": 6527410, + "B": 52061458 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + }, + { + "y": 0, + "z": 0, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 139633462211880, + "B": 1084380356156031 + }, + { + "y": 0, + "z": 0, + "A": 2784803785589, + "B": 38284735946769 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 6, + "orders": [ + { + "y": 0, + "z": 0, + "A": 729068024593520, + "B": 1583296743997440 + }, + { + "y": 0, + "z": 0, + "A": 761194234722, + "B": 8206335244012 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 898697149080734257151719, + "z": 926400260440038489828145, + "A": 6701695110995569, + "B": 6404666349060909 + }, + { + "y": 285634311, + "z": 3000000000, + "A": 8103120, + "B": 19903286 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 8, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 131363, + "A": 605842, + "B": 9214533 + }, + { + "y": 89849161686696130440, + "z": 89849161686696130440, + "A": 6093664678624880, + "B": 7250625960233619 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 140845070422535211267, + "A": 0, + "B": 0 + }, + { + "y": 100000000000000000000000, + "z": 100000000000000000000000, + "A": 0, + "B": 1641754040568763 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 6, + "orders": [ + { + "y": 158878602654380000000000, + "z": 158878602654380000000000, + "A": 0, + "B": 6653280447704807 + }, + { + "y": 0, + "z": 5560751092, + "A": 3636991, + "B": 41749485 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 522660927074165086179, + "z": 523845487865367615024, + "A": 0, + "B": 236208553282577 + }, + { + "y": 1504392204827311377, + "z": 665283769589016871081, + "A": 0, + "B": 440078071252594 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 1446598313106, + "B": 422212465065984 + }, + { + "y": 0, + "z": 0, + "A": 1432059267591, + "B": 278646010673002 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 0, + "A": 2773220111184, + "B": 278701756599472 + }, + { + "y": 0, + "z": 0, + "A": 2857983351848, + "B": 275788027321154 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 4872720563450129379744, + "z": 4872720563450129379744, + "A": 1565452094377504, + "B": 2526677720629248 + }, + { + "y": 8721068755009419, + "z": 8721068755009419, + "A": 18376077485, + "B": 888905200190 + } + ], + "pm_within_range": false, + "no_limit_orders": true, + "prices_overlap": false + }, + { + "tkn0_decimals": 9, + "tkn1_decimals": 18, + "orders": [ + { + "y": 1000000000000000000, + "z": 1000000000000000000, + "A": 131121824062538, + "B": 483017119822469 + }, + { + "y": 0, + "z": 0, + "A": 21475711489424, + "B": 85062356589755 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 27944043678451632222, + "z": 31375236383008632497438, + "A": 124122639063792, + "B": 825198779208894 + }, + { + "y": 2003426104079294116907, + "z": 2003426104079294116907, + "A": 7951602780796, + "B": 67201229626496 + } + ], + "pm_within_range": true, + "no_limit_orders": true, + "prices_overlap": true + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 0, + "z": 99202018334712002078841, + "A": 21194009032548, + "B": 819900276950757 + }, + { + "y": 5503700798507554270523, + "z": 5503700798507554270523, + "A": 1792355796027, + "B": 65408873830469 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 105852374456842007804, + "z": 105852374456842007804, + "A": 724558955263440, + "B": 1968690858451361 + }, + { + "y": 0, + "z": 25234716297622477, + "A": 183865313449, + "B": 2278035259754 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 380071000000000000000000, + "z": 380071000000000000000000, + "A": 3248923698638418, + "B": 3576390528861021 + }, + { + "y": 0, + "z": 32843568233268666, + "A": 26372844123, + "B": 68632319362 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + }, + { + "tkn0_decimals": 18, + "tkn1_decimals": 18, + "orders": [ + { + "y": 200000000000000000000000000000, + "z": 200000000000000000000000000000, + "A": 0, + "B": 19903286476643 + }, + { + "y": 0, + "z": 40000000000000000000000000000000, + "A": 0, + "B": 0 + } + ], + "pm_within_range": false, + "no_limit_orders": false, + "prices_overlap": false + } +] \ No newline at end of file diff --git a/fastlane_bot/tests/test_036_Manager.py b/fastlane_bot/tests/test_036_Manager.py index 511c76ff9..a2fcb8886 100644 --- a/fastlane_bot/tests/test_036_Manager.py +++ b/fastlane_bot/tests/test_036_Manager.py @@ -128,7 +128,7 @@ def test_test_update_from_event_carbon_v1_update(): event = event_data['carbon_v1_event_update'] manager.update_from_event(Event.from_dict(event_create_for_update)) - pools_to_add_from_contracts = [event[2].args['id'] for event in manager.pools_to_add_from_contracts] + pools_to_add_from_contracts = [event.args['id'] for event in manager.pools_to_add_from_contracts] assert event['args']['id'] in pools_to_add_from_contracts @@ -173,7 +173,6 @@ def test_test_update_from_event_carbon_v1_delete(): assert cid not in [pool['cid'] for pool in manager.pool_data] manager.update_from_event(Event.from_dict(event)) - assert cid in [p[-1] for p in manager.pools_to_add_from_contracts] fake_pool_data = { "address": event['address'], "cid": cid, diff --git a/fastlane_bot/tests/test_039_TestMultiMode.py b/fastlane_bot/tests/test_039_TestMultiMode.py deleted file mode 100644 index b3c875d70..000000000 --- a/fastlane_bot/tests/test_039_TestMultiMode.py +++ /dev/null @@ -1,251 +0,0 @@ -# ------------------------------------------------------------ -# Auto generated test file `test_039_TestMultiMode.py` -# ------------------------------------------------------------ -# source file = NBTest_039_TestMultiMode.py -# test id = 039 -# test comment = TestMultiMode -# ------------------------------------------------------------ - - - -""" -This module contains the tests for the exchanges classes -""" -from fastlane_bot import Bot, Config -from fastlane_bot.bot import CarbonBot -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.events.managers.manager import Manager -from fastlane_bot.events.interface import QueryInterface -from joblib import Parallel, delayed -import math -import json -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) -from fastlane_bot.testing import * -#plt.style.use('seaborn-dark') -plt.rcParams['figure.figsize'] = [12,6] -from fastlane_bot import __VERSION__ -require("3.0", __VERSION__) - - - -C = cfg = Config.new(config=Config.CONFIG_MAINNET) -cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN = 0.00001 -assert (C.NETWORK == C.NETWORK_MAINNET) -assert (C.PROVIDER == C.PROVIDER_ALCHEMY) -setup_bot = CarbonBot(ConfigObj=C) -pools = None -with open('fastlane_bot/tests/_data/latest_pool_data_testing.json') as f: - pools = json.load(f) -pools = [pool for pool in pools] -pools[0] -static_pools = pools -state = pools -exchanges = list({ex['exchange_name'] for ex in state}) -db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) -setup_bot.db = db - -static_pool_data_filename = "static_pool_data" - -static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) - -uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) - -tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) - -exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" - -exchanges = exchanges.split(",") - - -alchemy_max_block_fetch = 20 -static_pool_data["cid"] = [ - cfg.w3.keccak(text=f"{row['descr']}").hex() - for index, row in static_pool_data.iterrows() - ] -static_pool_data = [ - row for index, row in static_pool_data.iterrows() - if row["exchange_name"] in exchanges -] - -static_pool_data = pd.DataFrame(static_pool_data) -static_pool_data['exchange_name'].unique() -mgr = Manager( - web3=cfg.w3, - w3_async=cfg.w3_async, - cfg=cfg, - pool_data=static_pool_data.to_dict(orient="records"), - SUPPORTED_EXCHANGES=exchanges, - alchemy_max_block_fetch=alchemy_max_block_fetch, - uniswap_v2_event_mappings=uniswap_v2_event_mappings, - tokens=tokens.to_dict(orient="records"), -) - -start_time = time.time() -Parallel(n_jobs=-1, backend="threading")( - delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data -) -cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") - -mgr.deduplicate_pool_data() -cids = [pool["cid"] for pool in mgr.pool_data] -assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" -def init_bot(mgr: Manager) -> CarbonBot: - """ - Initializes the bot. - - Parameters - ---------- - mgr : Manager - The manager object. - - Returns - ------- - CarbonBot - The bot object. - """ - mgr.cfg.logger.info("Initializing the bot...") - bot = CarbonBot(ConfigObj=mgr.cfg) - bot.db = db - bot.db.mgr = mgr - assert isinstance( - bot.db, QueryInterface - ), "QueryInterface not initialized correctly" - return bot -bot = init_bot(mgr) -bot.db.remove_unmapped_uniswap_v2_pools() -bot.db.remove_zero_liquidity_pools() -bot.db.remove_unsupported_exchanges() -tokens = bot.db.get_tokens() -ADDRDEC = {t.address: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} -flashloan_tokens = bot.RUN_FLASHLOAN_TOKENS -CCm = bot.get_curves() -pools = db.get_pool_data_with_tokens() - -arb_mode = "multi" - - -# ------------------------------------------------------------ -# Test 039 -# File test_039_TestMultiMode.py -# Segment Test_TAX_TOKENS -# ------------------------------------------------------------ -def test_test_tax_tokens(): -# ------------------------------------------------------------ - - assert any(token.address in cfg.TAX_TOKENS for token in tokens), f"[TestMultiMode], DB does not include any tax tokens" - assert len(CCm) == 516, f"[NBTest 039 TestMultiMode] Expected 516 curves, found {len(CCm)}" - - for curve in CCm: - for token in cfg.TAX_TOKENS: - assert token not in [curve.params['tknx_addr'], curve.params['tkny_addr']], f"[TestMultiMode], curve {curve} includes tax token {token}" - - -# ------------------------------------------------------------ -# Test 039 -# File test_039_TestMultiMode.py -# Segment Test_MIN_PROFIT -# ------------------------------------------------------------ -def test_test_min_profit(): -# ------------------------------------------------------------ - - assert(cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN <= 0.0001), f"[TestMultiMode], default_min_profit_gas_token must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN}" - - -# ------------------------------------------------------------ -# Test 039 -# File test_039_TestMultiMode.py -# Segment Test_get_arb_finder -# ------------------------------------------------------------ -def test_test_get_arb_finder(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("multi") - assert arb_finder.__name__ == "FindArbitrageMultiPairwise", f"[TestMultiMode] Expected arb_finder class name name = FindArbitrageMultiPairwise, found {arb_finder.__name__}" - - -# ------------------------------------------------------------ -# Test 039 -# File test_039_TestMultiMode.py -# Segment Test_Combos_and_Tokens -# ------------------------------------------------------------ -def test_test_combos_and_tokens(): -# ------------------------------------------------------------ - - # + - assert len(CCm) == 516, f"[NBTest 039 TestMultiMode] Expected 516 curves, found {len(CCm)}" - arb_finder = bot._get_arb_finder("multi") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_TOKENS, - ConfigObj=bot.ConfigObj, - ) - 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)}" - assert type(combos) == list, f"[NBTest 039 TestMultiMode] combos is wrong data type. Expected list, found: {type(combos)}" - assert len(all_tokens) >= 234, f"[NBTest 039 TestMultiMode] Using wrong dataset, expected at least 234 tokens, found {len(all_tokens)}" - assert len(combos) >= 1398, f"[NBTest 039 TestMultiMode] Using wrong dataset, expected at least 1398 combos, found {len(combos)}" - # - - - -# ------------------------------------------------------------ -# Test 039 -# File test_039_TestMultiMode.py -# Segment Test_Expected_Output -# ------------------------------------------------------------ -def test_test_expected_output(): -# ------------------------------------------------------------ - - # + - assert len(CCm) == 516, f"[NBTest 039 TestMultiMode] Expected 516 curves, found {len(CCm)}" - arb_finder = bot._get_arb_finder("multi") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - - r = finder.find_arbitrage() - - multi_carbon_count = 0 - carbon_wrong_direction_count = 0 - for arb in r: - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb - if len(best_trade_instructions_dic) > 2: - multi_carbon_count += 1 - carbon_tkn_in = None - for trade in best_trade_instructions_dic: - if "-" in trade["cid"]: - if carbon_tkn_in is None: - carbon_tkn_in = trade["tknin"] - else: - if trade["tknin"] not in carbon_tkn_in: - carbon_wrong_direction_count += 1 - for ti in best_trade_instructions_dic: - for token in cfg.TAX_TOKENS: - assert token not in [ti['tknin'], ti['tknout']], f"[TestMultiMode], trade instruction {ti} includes tax token {token}" - - assert len(r) >= 27, f"[NBTest 039 TestMultiMode] Expected at least 27 arbs, found {len(r)}" - assert multi_carbon_count > 0, f"[NBTest 039 TestMultiMode] Not finding arbs with multiple Carbon curves." - assert carbon_wrong_direction_count == 0, f"[NBTest 039 TestMultiMode] Expected all Carbon curves to have the same tkn in and tkn out. Mixing is currently not supported." - # - - - \ No newline at end of file diff --git a/fastlane_bot/tests/test_040_TestSingleMode.py b/fastlane_bot/tests/test_040_TestSingleMode.py deleted file mode 100644 index c7a1ee890..000000000 --- a/fastlane_bot/tests/test_040_TestSingleMode.py +++ /dev/null @@ -1,197 +0,0 @@ -# ------------------------------------------------------------ -# Auto generated test file `test_040_TestSingleMode.py` -# ------------------------------------------------------------ -# source file = NBTest_040_TestSingleMode.py -# test id = 040 -# test comment = TestSingleMode -# ------------------------------------------------------------ - - - -""" -This module contains the tests for the exchanges classes -""" -from fastlane_bot import Bot, Config -from fastlane_bot.bot import CarbonBot -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.events.managers.manager import Manager -from fastlane_bot.events.interface import QueryInterface -from joblib import Parallel, delayed -import math -import json -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) -from fastlane_bot.testing import * - -#plt.style.use('seaborn-dark') -plt.rcParams['figure.figsize'] = [12,6] -from fastlane_bot import __VERSION__ -require("3.0", __VERSION__) - - - -C = cfg = Config.new(config=Config.CONFIG_MAINNET) -cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN = 0.00001 -assert (C.NETWORK == C.NETWORK_MAINNET) -assert (C.PROVIDER == C.PROVIDER_ALCHEMY) -setup_bot = CarbonBot(ConfigObj=C) -pools = None -with open('fastlane_bot/tests/_data/latest_pool_data_testing.json') as f: - pools = json.load(f) -pools = [pool for pool in pools] -pools[0] -static_pools = pools -state = pools -exchanges = list({ex['exchange_name'] for ex in state}) -db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) -setup_bot.db = db - -static_pool_data_filename = "static_pool_data" - -static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) - -uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) - -tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) - -exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" - -exchanges = exchanges.split(",") - - -alchemy_max_block_fetch = 20 -static_pool_data["cid"] = [ - cfg.w3.keccak(text=f"{row['descr']}").hex() - for index, row in static_pool_data.iterrows() - ] -static_pool_data = [ - row for index, row in static_pool_data.iterrows() - if row["exchange_name"] in exchanges -] - -static_pool_data = pd.DataFrame(static_pool_data) -static_pool_data['exchange_name'].unique() -mgr = Manager( - web3=cfg.w3, - w3_async=cfg.w3_async, - cfg=cfg, - pool_data=static_pool_data.to_dict(orient="records"), - SUPPORTED_EXCHANGES=exchanges, - alchemy_max_block_fetch=alchemy_max_block_fetch, - uniswap_v2_event_mappings=uniswap_v2_event_mappings, - tokens=tokens.to_dict(orient="records"), -) - -start_time = time.time() -Parallel(n_jobs=-1, backend="threading")( - delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data -) -cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") - -mgr.deduplicate_pool_data() -cids = [pool["cid"] for pool in mgr.pool_data] -assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" -def init_bot(mgr: Manager) -> CarbonBot: - """ - Initializes the bot. - - Parameters - ---------- - mgr : Manager - The manager object. - - Returns - ------- - CarbonBot - The bot object. - """ - mgr.cfg.logger.info("Initializing the bot...") - bot = CarbonBot(ConfigObj=mgr.cfg) - bot.db = db - bot.db.mgr = mgr - assert isinstance( - bot.db, QueryInterface - ), "QueryInterface not initialized correctly" - return bot -bot = init_bot(mgr) -bot.db.remove_unmapped_uniswap_v2_pools() -bot.db.remove_zero_liquidity_pools() -bot.db.remove_unsupported_exchanges() -tokens = bot.db.get_tokens() -ADDRDEC = {t.address: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} -flashloan_tokens = bot.RUN_FLASHLOAN_TOKENS -CCm = bot.get_curves() -pools = db.get_pool_data_with_tokens() - -arb_mode = "single" - -assert(cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN <= 0.0001), f"[TestSingleMode], default_min_profit_gas_token must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN}" - - - - - -# ------------------------------------------------------------ -# Test 040 -# File test_040_TestSingleMode.py -# Segment Test_arb_mode_class -# ------------------------------------------------------------ -def test_test_arb_mode_class(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("single") - assert arb_finder.__name__ == "FindArbitrageSinglePairwise", f"[TestSingleMode] Expected arb_finder class name name = FindArbitrageSinglePairwise, found {arb_finder.__name__}" - - -# ------------------------------------------------------------ -# Test 040 -# File test_040_TestSingleMode.py -# Segment Test_tokens_and_combos -# ------------------------------------------------------------ -def test_test_tokens_and_combos(): -# ------------------------------------------------------------ - - # + - arb_finder = bot._get_arb_finder("single") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_TOKENS, - ConfigObj=bot.ConfigObj, - ) - 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)}" - assert len(all_tokens) > 100, f"[TestSingleMode] Using wrong dataset, expected at least 100 tokens, found {len(all_tokens)}" - assert len(combos) > 1000, f"[TestSingleMode] Using wrong dataset, expected at least 100 combos, found {len(combos)}" - - arb_finder = bot._get_arb_finder("single") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - - for arb in r: - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb - assert len(best_trade_instructions_dic) <= 2, "[TestSingleMode] Expected arbs without multiple Carbon curves" - - assert len(r) >= 20, f"[TestSingleMode] Expected at least 20 arbs, found {len(r)}" - # - diff --git a/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py b/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py index b94c8b0dc..38c5bdc59 100644 --- a/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py +++ b/fastlane_bot/tests/test_042_TestBancorV3ModeTwoHop.py @@ -130,8 +130,6 @@ def init_bot(mgr: Manager) -> CarbonBot: CCm = bot.get_curves() pools = db.get_pool_data_with_tokens() -arb_mode = "b3_two_hop" - # ------------------------------------------------------------ # Test 042 @@ -144,18 +142,6 @@ def test_test_min_profit(): assert(cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN <= 0.0001), f"[test_bancor_v3_two_hop], default_min_profit_gas_token must be <= 0.0001 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN}" -# ------------------------------------------------------------ -# Test 042 -# File test_042_TestBancorV3ModeTwoHop.py -# Segment Test_arb_mode_class -# ------------------------------------------------------------ -def test_test_arb_mode_class(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("b3_two_hop") - assert arb_finder.__name__ == "ArbitrageFinderTriangleBancor3TwoHop", f"[test_bancor_v3_two_hop] Wrong Arb Finder class, expected ArbitrageFinderTriangleBancor3TwoHop, got {arb_finder.__name__}" - - # ------------------------------------------------------------ # Test 042 # File test_042_TestBancorV3ModeTwoHop.py @@ -164,245 +150,50 @@ def test_test_arb_mode_class(): def test_test_trade_merge(): # ------------------------------------------------------------ - arb_finder = bot._get_arb_finder("b3_two_hop") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=False, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = r - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token - ) - ordered_scaled_dcts = bot._basic_scaling( - ordered_trade_instructions_dct, best_src_token - ) - # Convert the trade instructions - ordered_trade_instructions_objects = bot._convert_trade_instructions( - ordered_scaled_dcts) - tx_route_handler = TxRouteHandler( - trade_instructions=ordered_trade_instructions_objects - ) - agg_trade_instructions = ( - tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) - if bot._carbon_in_trade_route(ordered_trade_instructions_objects) - else ordered_trade_instructions_objects - ) - # Calculate the trade instructions - calculated_trade_instructions = tx_route_handler.calculate_trade_outputs( - agg_trade_instructions - ) - assert len(calculated_trade_instructions) == 3 - # Aggregate multiple Bancor V3 trades into a single trade - calculated_trade_instructions = tx_route_handler.aggregate_bancor_v3_trades( - calculated_trade_instructions - ) - assert len(calculated_trade_instructions) == 2 - assert calculated_trade_instructions[0].tknin != "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" - assert calculated_trade_instructions[0].tknout != "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" - + arb_finder = bot.get_arb_finder("b3_two_hop", flashloan_tokens=flashloan_tokens, CCm=CCm) + arb_opp = arb_finder.find_arb_opps()[0] -# ------------------------------------------------------------ -# Test 042 -# File test_042_TestBancorV3ModeTwoHop.py -# Segment Test_get_optimal_arb_trade_amts -# ------------------------------------------------------------ -def test_test_get_optimal_arb_trade_amts(): -# ------------------------------------------------------------ - - # + - arb_finder = bot._get_arb_finder("b3_two_hop") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=False, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = r - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token - ) - - - pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] - first_check_pools = finder.get_exact_pools(pool_cids) - - assert first_check_pools[0].cid == pool_cids[0], f"[test_bancor_v3_two_hop] Validation, wrong first pool, expected CID: 0x7be3da0f8d0f70d8f7a84a08dd267beea4318ed1c9fb3d602b0f3a3c7bd1cf4a, got CID: {first_check_pools[0].cid}" - assert first_check_pools[1].cid == pool_cids[1], f"[test_bancor_v3_two_hop] Validation, wrong second pool, expected CID: 0x748ab2bef0d97e5a044268626e6c9c104bab818605d44f650fdeaa03a3c742d2, got CID: {first_check_pools[1].cid}" - assert first_check_pools[2].cid == pool_cids[2], f"[test_bancor_v3_two_hop] Validation, wrong third pool, expected CID: 0xb1d8cd62f75016872495dae3e19d96e364767e7d674488392029d15cdbcd7b34, got CID: {first_check_pools[2].cid}" - assert(len(first_check_pools) == 3), f"[test_bancor_v3_two_hop] Validation expected 3 pools, got {len(first_check_pools)}" - for pool in first_check_pools: - assert type(pool) == ConstantProductCurve, f"[test_bancor_v3_two_hop] Validation pool type mismatch, got {type(pool)} expected ConstantProductCurve" - assert pool.cid in pool_cids, f"[test_bancor_v3_two_hop] Validation missing pool.cid {pool.cid} in {pool_cids}" - - optimal_arb = finder.get_optimal_arb_trade_amts(pool_cids, 'DAI-1d0F') - assert type(optimal_arb) == float, f"[test_bancor_v3_two_hop] Optimal arb calculation type is {type(optimal_arb)} not float" - # - - + src_token = arb_opp["src_token"] + trade_instructions_dic = arb_opp["trade_instructions_dic"] -# ------------------------------------------------------------ -# Test 042 -# File test_042_TestBancorV3ModeTwoHop.py -# Segment Test_max_arb_trade_in_constant_product -# ------------------------------------------------------------ -def test_test_max_arb_trade_in_constant_product(): -# ------------------------------------------------------------ - - # + - arb_finder = bot._get_arb_finder("b3_two_hop") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=False, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = r - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token - ) - - - pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] - first_check_pools = finder.get_exact_pools(pool_cids) - flt='0x6B175474E89094C44Da98b954EedeAC495271d0F' - tkn0 = flt - tkn1 = finder.get_tkn(pool=first_check_pools[0], tkn_num=1) if finder.get_tkn(pool=first_check_pools[0], tkn_num=1) != flt else finder.get_tkn(pool=first_check_pools[0], tkn_num=0) - tkn2 = finder.get_tkn(pool=first_check_pools[1], tkn_num=0) if finder.get_tkn(pool=first_check_pools[1], tkn_num=0) == tkn1 else finder.get_tkn(pool=first_check_pools[1], tkn_num=1) - tkn3 = finder.get_tkn(pool=first_check_pools[1], tkn_num=0) if finder.get_tkn(pool=first_check_pools[1], tkn_num=0) != tkn1 else finder.get_tkn(pool=first_check_pools[1], tkn_num=1) - tkn5 = finder.get_tkn(pool=first_check_pools[2], tkn_num=1) if finder.get_tkn(pool=first_check_pools[2], tkn_num=1) == flt else finder.get_tkn(pool=first_check_pools[2], tkn_num=0) - p0t0 = first_check_pools[0].x if finder.get_tkn(pool=first_check_pools[0], tkn_num=0) == flt else first_check_pools[0].y - p0t1 = first_check_pools[0].y if finder.get_tkn(pool=first_check_pools[0], tkn_num=0) == flt else first_check_pools[0].x - p1t0 = first_check_pools[1].x if tkn1 == finder.get_tkn(pool=first_check_pools[1], tkn_num=0) else first_check_pools[1].y - p1t1 = first_check_pools[1].y if tkn1 == finder.get_tkn(pool=first_check_pools[1], tkn_num=0) else first_check_pools[1].x - p2t0 = first_check_pools[2].x if finder.get_tkn(pool=first_check_pools[2], tkn_num=0) != flt else first_check_pools[2].y - p2t1 = first_check_pools[2].y if finder.get_tkn(pool=first_check_pools[2], tkn_num=0) != flt else first_check_pools[2].x - fee0 = finder.get_fee_safe(first_check_pools[0].fee) - fee1 = finder.get_fee_safe(first_check_pools[1].fee) - fee2 = finder.get_fee_safe(first_check_pools[2].fee) - optimal_arb = finder.get_optimal_arb_trade_amts(pool_cids, '0x6B175474E89094C44Da98b954EedeAC495271d0F') - optimal_arb_low_level_check = finder.max_arb_trade_in_constant_product(p0t0=p0t0, p0t1=p0t1, p1t0=p1t0, p1t1=p1t1, p2t0=p2t0, p2t1=p2t1,fee0=fee0, fee1=fee1, fee2=fee2) - assert iseq(optimal_arb, optimal_arb_low_level_check), f"[test_bancor_v3_two_hop] Arb calculation result mismatch, pools likely ordered incorrectly, previous calc: {optimal_arb}, this calc: {optimal_arb_low_level_check}" - # max_arb_in = finder.max_arb_trade_in_constant_product(p0t0, p0t1, p1t0, p1t1, p2t0, p2t1, fee0=fee0, fee1=fee1, fee2=fee2) - # finder.ConfigObj.logger.info(f"\n\nfirst_check_pools: {first_check_pools}\n\nValidating trade, max_arb_in= {max_arb_in} {tkn0} -> {tkn1} -> {tkn3} -> {tkn5}, token amts: {p0t0, p0t1, p1t0, p1t1, p2t0, p2t1}, fees: {fee0, fee1, fee2}") - # - - + ordered_trade_instructions_dct = bot._simple_ordering_by_src_token(trade_instructions_dic, src_token) -# ------------------------------------------------------------ -# Test 042 -# File test_042_TestBancorV3ModeTwoHop.py -# Segment Test_get_fee_safe -# ------------------------------------------------------------ -def test_test_get_fee_safe(): -# ------------------------------------------------------------ - - # + - arb_finder = bot._get_arb_finder("b3_two_hop") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=False, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = r - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token + ordered_scaled_dcts = bot._basic_scaling(ordered_trade_instructions_dct, src_token) + # Convert the trade instructions + ordered_trade_instructions_objects = bot._convert_trade_instructions(ordered_scaled_dcts) + tx_route_handler = TxRouteHandler(trade_instructions=ordered_trade_instructions_objects) + trade_instructions = ( + tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) + if any(trade.is_carbon for trade in ordered_trade_instructions_objects) + else ordered_trade_instructions_objects ) - - pool_cids = [curve['cid'] for curve in ordered_trade_instructions_dct] - first_check_pools = finder.get_exact_pools(pool_cids) - ext_fee = finder.get_fee_safe(first_check_pools[0].fee) - - for pool in first_check_pools: - ext_fee = finder.get_fee_safe(pool.fee) - assert type(ext_fee) == float, f"[test_bancor_v3_two_hop] Testing external pool, fee type is {type(ext_fee)} not float" - # - - - -# ------------------------------------------------------------ -# Test 042 -# File test_042_TestBancorV3ModeTwoHop.py -# Segment Test_combos -# ------------------------------------------------------------ -def test_test_combos(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("b3_two_hop") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=False, - ConfigObj=bot.ConfigObj, - ) - #test_2_pools = [ConstantProductCurve(k=2921921249910.464, x=2760126.9934445512, x_act=2760126.9934445512, y_act=1058618.410258, pair='BNT-FF1C/USDC-eB48', cid='0xc4771395e1389e2e3a12ec22efbb7aff5b1c04e5ce9c7596a82e9dc8fdec725b', fee=0.0, descr='bancor_v3 BNT-FF1C/USDC-eB48 0.000', constr='uv2', params={'exchange': 'bancor_v3', 'tknx_dec': 18, 'tkny_dec': 6, 'tknx_addr': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17713739}), ConstantProductCurve(k=518129588.60853314, x=6351922.348885405, x_act=6351922.348885405, y_act=81.57051679, pair='BNT-FF1C/WBTC-C599', cid='0x3885d978c125e66686e3f678ab64d5b09e61f89bf6e87c9ff66e740fd06aeefa', fee=0.0, descr='bancor_v3 BNT-FF1C/WBTC-C599 0.000', constr='uv2', params={'exchange': 'bancor_v3', 'tknx_dec': 18, 'tkny_dec': 8, 'tknx_addr': '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 'tkny_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'blocklud': 17713739}), ConstantProductCurve(k=787603837541.6204, x=5107.692365701484, x_act=4.159867948255851, y_act=336571.44633978605, pair='WBTC-C599/USDC-eB48', cid='0x49ed97db2c080b7eac91dfaa7d51d5e8ac34c4dcfbcd3e8f2ed326a2a527b959', fee=0.003, descr='uniswap_v3 WBTC-C599/USDC-eB48 3000', constr='pkpp', params={'exchange': 'uniswap_v3', 'tknx_dec': 8, 'tkny_dec': 6, 'tknx_addr': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 'tkny_addr': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 'blocklud': 17713395, 'L': 887470.4713632})] - flt = {'MKR-79A2', 'TRAC-0A6F', 'MONA-412A', 'WBTC-C599', 'WOO-5D4B', 'MATIC-eBB0', 'BAT-87EF', 'UOS-5C8c', 'LRC-EafD', 'NMR-6671', 'DIP-cD83', 'TEMP-1aB9', 'ICHI-A881', 'USDC-eB48', 'ENS-9D72', 'vBNT-7f94', 'ANKR-EDD4', 'UNI-F984', 'REQ-938a', 'WETH-6Cc2', 'AAVE-DaE9', 'ENJ-3B9c', 'MANA-C942', 'wNXM-2bDE', 'QNT-4675', 'RLC-7375', 'CROWN-E0fa', 'CHZ-b4AF', 'USDT-1ec7', 'DAI-1d0F', 'RPL-A51f', 'HOT-26E2', 'LINK-86CA', 'wstETH-2Ca0'} - combos = finder.get_combos(flashloan_tokens=flt, CCm=CCm, arb_mode="b3_two_hop") - print(combos) - assert len(combos) >= 1122, "[test_bancor_v3_two_hop] Different data used for tests, expected 1122 combos" + # Calculate the trade instructions + tx_route_handler.calculate_trade_outputs(trade_instructions) + assert len(trade_instructions) == 3 + # Aggregate multiple Bancor V3 trades into a single trade + tx_route_handler.aggregate_bancor_v3_trades(trade_instructions) + assert len(trade_instructions) == 2 + assert trade_instructions[0].tknin != "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" + assert trade_instructions[0].tknout != "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C" # ------------------------------------------------------------ # Test 042 # File test_042_TestBancorV3ModeTwoHop.py -# Segment Test_get_miniverse_combos +# Segment Test_get_combos # ------------------------------------------------------------ -def test_test_get_miniverse_combos(): +def test_test_get_combos(): # ------------------------------------------------------------ - arb_finder = bot._get_arb_finder("b3_two_hop") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=False, - ConfigObj=bot.ConfigObj, - ) - flt = {"0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C","0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","0x514910771AF9Ca656af840dff83E8264EcF986CA"} - combos = finder.get_combos(flashloan_tokens=flt, CCm=CCm, arb_mode="b3_two_hop") - all_miniverses = finder.get_miniverse_combos(combos) - assert len(all_miniverses) >= 6, f"[test_bancor_v3_two_hop] Different data used for tests, expected 6 miniverses, found {len(all_miniverses)}" \ No newline at end of file + arb_finder = bot.get_arb_finder( + arb_mode = "b3_two_hop", + flashloan_tokens = { + "0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0x514910771AF9Ca656af840dff83E8264EcF986CA" + }, + CCm = CCm + ) + combos = arb_finder.get_combos() + assert len(combos) >= 6, f"[test_bancor_v3_two_hop] Different data used for tests, expected 6 combos, found {len(combos)}" diff --git a/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py b/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py index f49c68958..27f7247dd 100644 --- a/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py +++ b/fastlane_bot/tests/test_043_TestEmptyCarbonOrders.py @@ -138,8 +138,6 @@ def init_bot(mgr: Manager) -> CarbonBot: CCm = bot.get_curves() pools = db.get_pool_data_with_tokens() -arb_mode = "multi" - # ------------------------------------------------------------ # Test 043 @@ -150,60 +148,46 @@ def test_test_empty_carbon_orders_removed(): # ------------------------------------------------------------ # + - arb_finder = bot._get_arb_finder("multi") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = r[11] + arb_finder = bot.get_arb_finder("multi_pairwise_all", flashloan_tokens=flashloan_tokens, CCm=CCm) + arb_opp = arb_finder.find_arb_opps()[0] + + src_token = arb_opp["src_token"] - best_trade_instructions_dic # Check that this gets filtered out - test_trade = [{ - 'cid': '0x0aadab62b703c91233e4215054caa98283a6cdc65364a8848fc645008c24a053', - # 'strategy_id': 0, - 'tknin': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', - 'amtin': 0.008570336169213988, - 'tknout': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', - 'amtout': -0.13937506393995136, - 'error': None}, - {'cid': '9187623906865338513511114400657741709420-1', - 'strategy_id': 9187623906865338513511114400657741709420, - 'tknin': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', - 'amtin': 0, - 'tknout': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', - 'amtout': 0, - 'error': None}, - {'cid': '9187623906865338513511114400657741709458-1', - 'strategy_id': 9187623906865338513511114400657741709458, - 'tknin': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', - 'amtin': 0.13937506393995136, - 'tknout': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', - 'amtout': 0.008870336169213988, - 'error': None}] - - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - test_trade, best_src_token - ) - print(f"ordered_trade_instructions_dct: {ordered_trade_instructions_dct}") - ordered_scaled_dcts = bot._basic_scaling( - ordered_trade_instructions_dct, best_src_token - ) + test_trade = [ + { + 'cid': '0x0aadab62b703c91233e4215054caa98283a6cdc65364a8848fc645008c24a053', + # 'strategy_id': 0, + 'tknin': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + 'amtin': 0.008570336169213988, + 'tknout': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + 'amtout': -0.13937506393995136, + 'error': None + }, + { + 'cid': '9187623906865338513511114400657741709420-1', + 'strategy_id': 9187623906865338513511114400657741709420, + 'tknin': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + 'amtin': 0, + 'tknout': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + 'amtout': 0, + 'error': None + }, + { + 'cid': '9187623906865338513511114400657741709458-1', + 'strategy_id': 9187623906865338513511114400657741709458, + 'tknin': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + 'amtin': 0.13937506393995136, + 'tknout': '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + 'amtout': 0.008870336169213988, + 'error': None + } + ] + ordered_trade_instructions_dct = bot._simple_ordering_by_src_token(test_trade, src_token) + + ordered_scaled_dcts = bot._basic_scaling(ordered_trade_instructions_dct, src_token) + ordered_scaled_dcts[0]["tknin_dec_override"] = 8 ordered_scaled_dcts[0]["tknout_dec_override"] = 18 ordered_scaled_dcts[0]["exchange_override"] = "uniswap_v2" @@ -213,36 +197,26 @@ def test_test_empty_carbon_orders_removed(): ordered_scaled_dcts[2]["tknin_dec_override"] = 18 ordered_scaled_dcts[2]["tknout_dec_override"] = 8 ordered_scaled_dcts[2]["exchange_override"] = "carbon_v1" - - print(f"ordered_scaled_dcts: {ordered_scaled_dcts}") - + ordered_trade_instructions_objects = bot._convert_trade_instructions(ordered_scaled_dcts, ) # print(f"ordered_trade_instructions_objects: {ordered_trade_instructions_objects}") - tx_route_handler = TxRouteHandler( - trade_instructions=ordered_trade_instructions_objects - ) - agg_trade_instructions = ( - tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) - if bot._carbon_in_trade_route(ordered_trade_instructions_objects) - else ordered_trade_instructions_objects - ) - print(f"agg_trade_instructions: {agg_trade_instructions[0]}") - # Calculate the trade instructions - calculated_trade_instructions = tx_route_handler.calculate_trade_outputs( - agg_trade_instructions + tx_route_handler = TxRouteHandler(trade_instructions=ordered_trade_instructions_objects) + trade_instructions = ( + tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) + if any(trade.is_carbon for trade in ordered_trade_instructions_objects) + else ordered_trade_instructions_objects ) - encoded_trade_instructions = tx_route_handler.custom_data_encoder( - calculated_trade_instructions - ) + # Calculate the trade instructions + tx_route_handler.calculate_trade_outputs(trade_instructions) + + tx_route_handler.custom_data_encoder(trade_instructions) deadline = bot._get_deadline(1) # Get the route struct route_struct = [ asdict(rs) - for rs in tx_route_handler.get_route_structs( - encoded_trade_instructions, deadline - ) + for rs in tx_route_handler.get_route_structs(trade_instructions, deadline) ] for route in route_struct: if route["platformId"] == 6: diff --git a/fastlane_bot/tests/test_045_Validator.py b/fastlane_bot/tests/test_045_Validator.py deleted file mode 100644 index ca8732097..000000000 --- a/fastlane_bot/tests/test_045_Validator.py +++ /dev/null @@ -1,152 +0,0 @@ -# ------------------------------------------------------------ -# Auto generated test file `test_045_Validator.py` -# ------------------------------------------------------------ -# source file = NBTest_045_Validator.py -# test id = 045 -# test comment = Validator -# ------------------------------------------------------------ - - - -""" -This module contains the tests for the exchanges classes -""" -from fastlane_bot import Bot, Config -from fastlane_bot.bot import CarbonBot -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.events.managers.manager import Manager -from fastlane_bot.events.interface import QueryInterface -from joblib import Parallel, delayed -import math -import json -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) -from fastlane_bot.testing import * - -#plt.style.use('seaborn-dark') -plt.rcParams['figure.figsize'] = [12,6] -from fastlane_bot import __VERSION__ -require("3.0", __VERSION__) - - - -C = cfg = Config.new(config=Config.CONFIG_MAINNET) -cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN = 0.00001 -assert (C.NETWORK == C.NETWORK_MAINNET) -assert (C.PROVIDER == C.PROVIDER_ALCHEMY) -setup_bot = CarbonBot(ConfigObj=C) -pools = None -with open('fastlane_bot/tests/_data/latest_pool_data_testing.json') as f: - pools = json.load(f) -pools = [pool for pool in pools] -pools[0] -static_pools = pools -state = pools -exchanges = list({ex['exchange_name'] for ex in state}) -db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) -setup_bot.db = db - -static_pool_data_filename = "static_pool_data" - -static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) - -uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) - -tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) - -exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" - -exchanges = exchanges.split(",") - - -alchemy_max_block_fetch = 20 -static_pool_data["cid"] = [ - cfg.w3.keccak(text=f"{row['descr']}").hex() - for index, row in static_pool_data.iterrows() - ] -static_pool_data = [ - row for index, row in static_pool_data.iterrows() - if row["exchange_name"] in exchanges -] - -static_pool_data = pd.DataFrame(static_pool_data) -static_pool_data['exchange_name'].unique() -mgr = Manager( - web3=cfg.w3, - w3_async=cfg.w3_async, - cfg=cfg, - pool_data=static_pool_data.to_dict(orient="records"), - SUPPORTED_EXCHANGES=exchanges, - alchemy_max_block_fetch=alchemy_max_block_fetch, - uniswap_v2_event_mappings=uniswap_v2_event_mappings, - tokens=tokens.to_dict(orient="records"), -) - -start_time = time.time() -Parallel(n_jobs=-1, backend="threading")( - delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data -) -cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") - -mgr.deduplicate_pool_data() -cids = [pool["cid"] for pool in mgr.pool_data] -assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" -def init_bot(mgr: Manager) -> CarbonBot: - """ - Initializes the bot. - - Parameters - ---------- - mgr : Manager - The manager object. - - Returns - ------- - CarbonBot - The bot object. - """ - mgr.cfg.logger.info("Initializing the bot...") - bot = CarbonBot(ConfigObj=mgr.cfg) - bot.db = db - bot.db.mgr = mgr - assert isinstance( - bot.db, QueryInterface - ), "QueryInterface not initialized correctly" - return bot -bot = init_bot(mgr) -bot.db.remove_unmapped_uniswap_v2_pools() -bot.db.remove_zero_liquidity_pools() -bot.db.remove_unsupported_exchanges() -tokens = bot.db.get_tokens() -ADDRDEC = {t.address: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} -flashloan_tokens = bot.RUN_FLASHLOAN_TOKENS -CCm = bot.get_curves() -pools = db.get_pool_data_with_tokens() - - -# ------------------------------------------------------------ -# Test 045 -# File test_045_Validator.py -# Segment Test_validator -# ------------------------------------------------------------ -def test_test_validator(): -# ------------------------------------------------------------ - for arb_mode in ["single", "multi", "multi_triangle"]: - arb_finder = bot._get_arb_finder(arb_mode) - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - arb_opp = r[0] - validated = bot.validate_optimizer_trades(arb_opp=arb_opp, arb_finder=finder) - assert arb_opp == validated diff --git a/fastlane_bot/tests/test_047_Randomizer.py b/fastlane_bot/tests/test_047_Randomizer.py deleted file mode 100644 index b2f250575..000000000 --- a/fastlane_bot/tests/test_047_Randomizer.py +++ /dev/null @@ -1,211 +0,0 @@ -# ------------------------------------------------------------ -# Auto generated test file `test_047_Randomizer.py` -# ------------------------------------------------------------ -# source file = NBTest_047_Randomizer.py -# test id = 047 -# test comment = Randomizer -# ------------------------------------------------------------ - - - -""" -This module contains the tests for the exchanges classes -""" -from fastlane_bot import Bot, Config -from fastlane_bot.bot import CarbonBot -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.events.managers.manager import Manager -from fastlane_bot.events.interface import QueryInterface -from joblib import Parallel, delayed -import math -import json -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV3)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CarbonV1)) -print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(BancorV3)) -from fastlane_bot.testing import * -#plt.style.use('seaborn-dark') -plt.rcParams['figure.figsize'] = [12,6] -from fastlane_bot import __VERSION__ -require("3.0", __VERSION__) - - - -C = cfg = Config.new(config=Config.CONFIG_MAINNET) -cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN = 0.00001 -assert (C.NETWORK == C.NETWORK_MAINNET) -assert (C.PROVIDER == C.PROVIDER_ALCHEMY) -setup_bot = CarbonBot(ConfigObj=C) -pools = None -with open('fastlane_bot/tests/_data/latest_pool_data_testing.json') as f: - pools = json.load(f) -pools = [pool for pool in pools] -pools[0] -static_pools = pools -state = pools -exchanges = list({ex['exchange_name'] for ex in state}) -db = QueryInterface(state=state, ConfigObj=C, exchanges=exchanges) -setup_bot.db = db - -static_pool_data_filename = "static_pool_data" - -static_pool_data = pd.read_csv(f"fastlane_bot/data/{static_pool_data_filename}.csv", low_memory=False) - -uniswap_v2_event_mappings = pd.read_csv("fastlane_bot/data/uniswap_v2_event_mappings.csv", low_memory=False) - -tokens = pd.read_csv("fastlane_bot/data/tokens.csv", low_memory=False) - -exchanges = "carbon_v1,bancor_v3,uniswap_v3,uniswap_v2,sushiswap_v2" - -exchanges = exchanges.split(",") - - -alchemy_max_block_fetch = 20 -static_pool_data["cid"] = [ - cfg.w3.keccak(text=f"{row['descr']}").hex() - for index, row in static_pool_data.iterrows() - ] -static_pool_data = [ - row for index, row in static_pool_data.iterrows() - if row["exchange_name"] in exchanges -] - -static_pool_data = pd.DataFrame(static_pool_data) -static_pool_data['exchange_name'].unique() -mgr = Manager( - web3=cfg.w3, - w3_async=cfg.w3_async, - cfg=cfg, - pool_data=static_pool_data.to_dict(orient="records"), - SUPPORTED_EXCHANGES=exchanges, - alchemy_max_block_fetch=alchemy_max_block_fetch, - uniswap_v2_event_mappings=uniswap_v2_event_mappings, - tokens=tokens.to_dict(orient="records"), -) - -start_time = time.time() -Parallel(n_jobs=-1, backend="threading")( - delayed(mgr.add_pool_to_exchange)(row) for row in mgr.pool_data -) -cfg.logger.info(f"Time taken to add initial pools: {time.time() - start_time}") - -mgr.deduplicate_pool_data() -cids = [pool["cid"] for pool in mgr.pool_data] -assert len(cids) == len(set(cids)), "duplicate cid's exist in the pool data" -def init_bot(mgr: Manager) -> CarbonBot: - """ - Initializes the bot. - - Parameters - ---------- - mgr : Manager - The manager object. - - Returns - ------- - CarbonBot - The bot object. - """ - mgr.cfg.logger.info("Initializing the bot...") - bot = CarbonBot(ConfigObj=mgr.cfg) - bot.db = db - bot.db.mgr = mgr - assert isinstance( - bot.db, QueryInterface - ), "QueryInterface not initialized correctly" - return bot -bot = init_bot(mgr) -bot.db.remove_unmapped_uniswap_v2_pools() -bot.db.remove_zero_liquidity_pools() -bot.db.remove_unsupported_exchanges() -tokens = bot.db.get_tokens() -ADDRDEC = {t.address: (t.address, int(t.decimals)) for t in tokens if not math.isnan(t.decimals)} -flashloan_tokens = bot.RUN_FLASHLOAN_TOKENS -CCm = bot.get_curves() -pools = db.get_pool_data_with_tokens() - -arb_mode = "multi" - - -# ------------------------------------------------------------ -# Test 047 -# File test_047_Randomizer.py -# Segment Test_randomizer -# ------------------------------------------------------------ -def test_test_randomizer(): -# ------------------------------------------------------------ - - # + - arb_finder = bot._get_arb_finder("multi") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - - #arb_opp = r[0] - - - assert len(r) >= 26, f"[NB047 Randomizer], expected at least 26 arbs, found {len(r)}" - - - arb_opp_0 = bot.randomize(arb_opps=r, randomizer=0) - arb_opp_1 = bot.randomize(arb_opps=r, randomizer=1) - 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) - - 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)}" - assert len(arb_opp_2) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_2)}" - assert len(arb_opp_3) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_3)}" - assert len(arb_opp_25) == 5, f"[NB047 Randomizer], expected 1 arb back from randomizer with length of 5, found length of {len(arb_opp_25)}" - assert isinstance(np.float64(arb_opp_0[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_0[0])}" - assert isinstance(np.float64(arb_opp_1[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_1[0])}" - assert isinstance(np.float64(arb_opp_2[0]), np.floating), f"[NB047 Randomizer], expected first value back from randomizer to be of type np.float64, found type {type(arb_opp_2[0])}" - # - - - 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])}" - - 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])}" - - -# ------------------------------------------------------------ -# Test 047 -# File test_047_Randomizer.py -# Segment Test_sorted_by_profit -# ------------------------------------------------------------ -def test_test_sorted_by_profit(): -# ------------------------------------------------------------ - - # + - arb_opps = [(2.6927646232907136, [{'cid': '0xe37abfaee752c24a764955cbb2d10c3c9f88472263cbd2c00ca57facb0f128fe', 'tknin': 'WETH-6Cc2', 'amtin': 0.003982724863828224, 'tknout': 'BNT-FF1C', 'amtout': -19.27862435251882, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] - ), (2.5352758371554955, [{'cid': '0x748ab2bef0d97e5a044268626e6c9c104bab818605d44f650fdeaa03a3c742d2', 'tknin': 'WETH-6Cc2', 'amtin': 0.003982718826136988, 'tknout': 'BNT-FF1C', 'amtout': -19.1211355663836, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] - ), (1.9702345513100596, [{'cid': '0xc4771395e1389e2e3a12ec22efbb7aff5b1c04e5ce9c7596a82e9dc8fdec725b', 'tknin': 'BNT-FF1C', 'amtin': 750.6057364856824, 'tknout': 'USDC-eB48', 'amtout': -293.5068652469199, 'error': None}, {'cid': '2381976568446569244243622252022377480332-1', 'tknin': 'USDC-eB48', 'amtin': 292.73623752593994, 'tknout': 'BNT-FF1C', 'amtout': -750.6057367324829, 'error': None}] - ), (2.67115241495777, [{'cid': '0xe37abfaee752c24a764955cbb2d10c3c9f88472263cbd2c00ca57facb0f128fe', 'tknin': 'WETH-6Cc2', 'amtin': 0.0034263543081607395, 'tknout': 'BNT-FF1C', 'amtout': -16.58585974665766, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] - ), (2.535310217715329, [{'cid': '0x748ab2bef0d97e5a044268626e6c9c104bab818605d44f650fdeaa03a3c742d2', 'tknin': 'WETH-6Cc2', 'amtin': 0.003454648687693407, 'tknout': 'BNT-FF1C', 'amtout': -16.58585971966386, 'error': None}, {'cid': '3743106036130323098097120681749450326076-0', 'tknin': 'BNT-FF1C', 'amtin': 16.585859729228105, 'tknout': 'WETH-6Cc2', 'amtout': -0.003982724874543209, 'error': None}] - ), (5.438084583685771, [{'cid': '0x8f9771f2886aa12c1659c275b8e305f58c7c41ba82df03bb21c0bcac98ffde4b', 'tknin': 'WETH-6Cc2', 'amtin': 0.002847350733645726, 'tknout': 'HEX-eb39', 'amtout': -556.3312638401985, 'error': None}, {'cid': '14291859410679415465461733512134264881242-0', 'tknin': 'HEX-eb39', 'amtin': 556.3312644516602, 'tknout': 'WETH-6Cc2', 'amtout': -0.003980041696137606, 'error': None}] - ), (5.400385044154462, [{'cid': '0x3a98798837e610ac07762e2d58f29f0cf96297a2528f86e0fe9b903b1e45a204', 'tknin': 'WETH-6Cc2', 'amtin': 0.0028413006787388895, 'tknout': 'HEX-eb39', 'amtout': -553.6187023743987, 'error': None}, {'cid': '14291859410679415465461733512134264881242-0', 'tknin': 'HEX-eb39', 'amtin': 553.6187027173414, 'tknout': 'WETH-6Cc2', 'amtout': -0.003966139257351835, 'error': None}] - ), (1.9713220433332026, [{'cid': '0xc4771395e1389e2e3a12ec22efbb7aff5b1c04e5ce9c7596a82e9dc8fdec725b', 'tknin': 'BNT-FF1C', 'amtin': 748.6344146891497, 'tknout': 'USDC-eB48', 'amtout': -292.73623879346997, 'error': None}, {'cid': '2381976568446569244243622252022377480332-1', 'tknin': 'USDC-eB48', 'amtin': 292.73623752593994, 'tknout': 'BNT-FF1C', 'amtout': -750.6057367324829, 'error': None}] - ), (8.465616944048316, [{'cid': '0x5b5f170977fe879c965a9fec9aeba4dfe29659f503cd5fe6e67349bdc3089295', 'tknin': '0x0-1AD5', 'amtin': 359.7323400862515, 'tknout': 'WETH-6Cc2', 'amtout': -0.0070300615800533706, 'error': None}, {'cid': '9868188640707215440437863615521278132277-1', 'tknin': 'WETH-6Cc2', 'amtin': 0.00526677017535393, 'tknout': '0x0-1AD5', 'amtout': -359.73234041399974, 'error': None}] - ), (6.717558869249757, [{'cid': '0x1eda42a2cced5e9cfffe1b15d7c39253514267401c5bd2e9ca28287f8a996fde', 'tknin': 'rETH-6393', 'amtin': 0.2496827895520255, 'tknout': 'WETH-6Cc2', 'amtout': -0.26914170442614704, 'error': None}, {'cid': '3062541302288446171170371466885913903202-0', 'tknin': 'WETH-6Cc2', 'amtin': 0.267742513570596, 'tknout': 'rETH-6393', 'amtout': -0.2496827897163172, 'error': None}] - )] - - ops = bot.randomize(arb_opps=arb_opps, randomizer=3) - - assert iseq(ops[0], 8.465616944048316) or iseq(ops[0], 6.717558869249757) or iseq(ops[0], 5.438084583685771), f"[NB047 Randomizer], expected randomizer to return top 3 most profitable arbs, but it did not!" - # - - - \ No newline at end of file diff --git a/fastlane_bot/tests/test_048_RespectFlashloanTokensClickParam.py b/fastlane_bot/tests/test_048_RespectFlashloanTokensClickParam.py index 7169c7af0..e4cbe7b97 100644 --- a/fastlane_bot/tests/test_048_RespectFlashloanTokensClickParam.py +++ b/fastlane_bot/tests/test_048_RespectFlashloanTokensClickParam.py @@ -7,15 +7,13 @@ # ------------------------------------------------------------ - """ -This module contains the tests which ensure that the flashloan tokens click parameters are respected. +This module contains the tests the `multi_pairwise_all` arb-mode with the `flashloan_tokens` parameter. """ from fastlane_bot import Bot from fastlane_bot.tools.cpc import ConstantProductCurve as CPC from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 -import subprocess, os, sys -import pytest +import subprocess, os print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) @@ -28,8 +26,6 @@ require("3.0", __VERSION__) - - def find_main_py(): # Start at the directory of the current script cwd = os.path.abspath(os.path.join(os.getcwd())) @@ -50,38 +46,24 @@ def find_main_py(): cwd = new_cwd -def run_command(arb_mode): +# ------------------------------------------------------------ +# Test 048 +# File test_048_RespectFlashloanTokensClickParam.py +# Segment Test multi_pairwise_all with flashloan tokens +# ------------------------------------------------------------ +def test_test_multi_pairwise_all_with_flashloan_tokens(): +# ------------------------------------------------------------ - # Find the correct path to main.py - main_script_path = find_main_py() - print(f"Found main.py in {main_script_path}") - main_script_path = os.path.normpath(main_script_path + "/main.py") - - # Run the command cmd = [ "python", - main_script_path, - f"--arb_mode={arb_mode}", + find_main_py() + "/main.py", + f"--arb_mode=multi_pairwise_all", "--default_min_profit_gas_token=0.001", - "--limit_bancor3_flashloan_tokens=False", "--use_cached_events=False", - "--timeout=1", - "--loglevel=DEBUG", + "--timeout=120", "--flashloan_tokens='0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C,0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD'", "--blockchain=ethereum" ] - expected_log_line = """Flashloan tokens are set as: ["'0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C", 'ETH', "0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD'"]""" result = subprocess.run(cmd, text=True, capture_output=True, check=True) - assert expected_log_line in result.stderr, result.stderr - - -# ------------------------------------------------------------ -# Test 048 -# File test_048_RespectFlashloanTokensClickParam.py -# Segment Test flashloan_tokens is Respected -# ------------------------------------------------------------ -def test_test_flashloan_tokens_is_respected(): -# ------------------------------------------------------------ - - run_command("multi") \ No newline at end of file + assert result.returncode == 0 diff --git a/fastlane_bot/tests/test_050_TestBancorV2.py b/fastlane_bot/tests/test_050_TestBancorV2.py index 45a036f8f..0182cbd44 100644 --- a/fastlane_bot/tests/test_050_TestBancorV2.py +++ b/fastlane_bot/tests/test_050_TestBancorV2.py @@ -20,7 +20,7 @@ from fastlane_bot.events.managers.manager import Manager from fastlane_bot.events.interface import QueryInterface from joblib import Parallel, delayed -from fastlane_bot.tools.cpc import ConstantProductCurve as CPC, T +from fastlane_bot.tools.cpc import ConstantProductCurve as CPC from dataclasses import asdict import math import json @@ -132,8 +132,6 @@ def init_bot(mgr: Manager) -> CarbonBot: CCm = bot.get_curves() pools = db.get_pool_data_with_tokens() -arb_mode = "multi" - # ------------------------------------------------------------ # Test 050 @@ -155,18 +153,8 @@ def test_test_min_profit(): def test_test_combos_and_tokens(): # ------------------------------------------------------------ - arb_finder = bot._get_arb_finder("multi") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_TOKENS, - ConfigObj=bot.ConfigObj, - ) - 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)}" + arb_finder = bot.get_arb_finder("multi_pairwise_all", flashloan_tokens=flashloan_tokens, CCm=CCm) + combos = arb_finder.find_combos() assert len(combos) > 1000, f"[NBTest_50_TestBancorV2] Using wrong dataset, expected at least 100 combos, found {len(combos)}" @@ -179,123 +167,64 @@ def test_test_combos_and_tokens(): def test_test_expected_output_bancorv2(): # ------------------------------------------------------------ - # + - arb_finder = bot._get_arb_finder("multi_pairwise_all") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - - arb_with_bancor_v2 = [] - for arb_opp in r: - pools = [] - for pool in arb_opp[2]: - pools += [curve for curve in CCm if curve.cid == pool['cid']] - for pool in pools: - if pool.params['exchange'] == "bancor_v2": - arb_with_bancor_v2.append(arb_opp) - - assert len(r) >= 27, f"[NBTest_50_TestBancorV2] Expected at least 27 arb opps, found {len(r)}" - assert len(arb_with_bancor_v2) >= 3, f"[NBTest_50_TestBancorV2] Expected at least 3 arb opps with Bancor V2 pools, found {len(arb_with_bancor_v2)}" - - # + - arb_finder = bot._get_arb_finder("multi_pairwise_all") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() + arb_finder = bot.get_arb_finder("multi_pairwise_all", flashloan_tokens=flashloan_tokens, CCm=CCm) + arb_opps = arb_finder.find_arb_opps() + arb_with_bancor_v2 = [] - for arb_opp in r: + for arb_opp in arb_opps: pools = [] - for pool in arb_opp[2]: + for pool in arb_opp["trade_instructions_dic"]: pools += [curve for curve in CCm if curve.cid == pool['cid']] for pool in pools: if pool.params['exchange'] == "bancor_v2": arb_with_bancor_v2.append(arb_opp) + assert len(arb_opps) > 30, f"[NBTest_50_TestBancorV2] Expected at least 30 arb opps, found {len(arb_opps)}" + assert len(arb_with_bancor_v2) >= 3, f"[NBTest_50_TestBancorV2] Expected at least 3 arb opps with Bancor V2 pools, found {len(arb_with_bancor_v2)}" + # get specific arb for tests test_arb = arb_with_bancor_v2[0] - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = test_arb + src_token = test_arb["src_token"] + trade_instructions_dic = test_arb["trade_instructions_dic"] # Order the trade instructions - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token - ) + ordered_trade_instructions_dct = bot._simple_ordering_by_src_token(trade_instructions_dic, src_token) # Scale the trade instructions - ordered_scaled_dcts = bot._basic_scaling( - ordered_trade_instructions_dct, best_src_token - ) + ordered_scaled_dcts = bot._basic_scaling(ordered_trade_instructions_dct, src_token) # Convert the trade instructions - ordered_trade_instructions_objects = bot._convert_trade_instructions( - ordered_scaled_dcts - ) + ordered_trade_instructions_objects = bot._convert_trade_instructions(ordered_scaled_dcts) # Create the tx route handler - tx_route_handler = TxRouteHandler( - trade_instructions=ordered_trade_instructions_objects - ) + tx_route_handler = TxRouteHandler(trade_instructions=ordered_trade_instructions_objects) # Aggregate the carbon trades - agg_trade_instructions = ( + trade_instructions = ( tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) - if bot._carbon_in_trade_route(ordered_trade_instructions_objects) + if any(trade.is_carbon for trade in ordered_trade_instructions_objects) else ordered_trade_instructions_objects ) # Calculate the trade instructions - calculated_trade_instructions = tx_route_handler.calculate_trade_outputs( - agg_trade_instructions - ) + tx_route_handler.calculate_trade_outputs(trade_instructions) # Aggregate multiple Bancor V3 trades into a single trade - calculated_trade_instructions = tx_route_handler.aggregate_bancor_v3_trades( - calculated_trade_instructions - ) + tx_route_handler.aggregate_bancor_v3_trades(trade_instructions) # Get the flashloan token - fl_token = fl_token_with_weth = calculated_trade_instructions[0].tknin_address + fl_token = trade_instructions[0].tknin_address # If the flashloan token is WETH, then use ETH - if fl_token == T.WETH: - fl_token = T.NATIVE_ETH - - 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( - CCm, best_profit, fl_token, - ) + if fl_token == C.network.WETH_ADDRESS: + fl_token = C.network.ETH_ADDRESS # Get the flashloan amount and token address - 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 - ) + flashloan_amount = int(trade_instructions[0].amtin_wei) # Encode the trade instructions - encoded_trade_instructions = tx_route_handler.custom_data_encoder( - calculated_trade_instructions - ) + tx_route_handler.custom_data_encoder(trade_instructions) # Get the deadline deadline = bot._get_deadline(1) @@ -304,17 +233,9 @@ def test_test_expected_output_bancorv2(): route_struct = [ asdict(rs) for rs in tx_route_handler.get_route_structs( - encoded_trade_instructions, deadline + trade_instructions, deadline ) ] - b2pools = [pool['anchor'] for pool in mgr.pool_data if pool["exchange_name"] in "bancor_v2"] bancor_v2_converter_addresses = [pool["anchor"] for pool in state if pool["exchange_name"] in "bancor_v2"] - assert arb_finder.__name__ == "FindArbitrageMultiPairwiseAll", f"[NBTest_50_TestBancorV2] Expected arb_finder class name name = FindArbitrageMultiPairwise, found {arb_finder.__name__}" - assert len(r) > 30, f"[NBTest_50_TestBancorV2] Expected at least 30 arb opps, found {len(r)}" - assert len(arb_with_bancor_v2) >= 3, f"[NBTest_50_TestBancorV2] Expected at least 3 arb opps with Bancor V2 pools, found {len(arb_with_bancor_v2)}" - assert encoded_trade_instructions[0].amtin_wei == flashloan_amount, f"[NBTest_50_TestBancorV2] First trade in should match flashloan amount, {encoded_trade_instructions[0].amtin_wei} does not = {flashloan_amount}" + assert trade_instructions[0].amtin_wei == flashloan_amount, f"[NBTest_50_TestBancorV2] First trade in should match flashloan amount, {trade_instructions[0].amtin_wei} does not = {flashloan_amount}" assert route_struct[0]['customAddress'] in bancor_v2_converter_addresses or route_struct[1]['customAddress'] in bancor_v2_converter_addresses, f"[NBTest_50_TestBancorV2] customAddress for Bancor V2.1 trade must be converter token address, expected: anchor for Bancor V2 pool for one address, found: {route_struct[0]['customAddress']} and {route_struct[1]['customAddress']}" - # - - - - \ No newline at end of file diff --git a/fastlane_bot/tests/test_058_BalancerIntegration.py b/fastlane_bot/tests/test_058_BalancerIntegration.py index ab5fde773..9dbff5fd6 100644 --- a/fastlane_bot/tests/test_058_BalancerIntegration.py +++ b/fastlane_bot/tests/test_058_BalancerIntegration.py @@ -136,8 +136,6 @@ def init_bot(mgr: Manager) -> CarbonBot: CCm = bot.get_curves() pools = db.get_pool_data_with_tokens() -arb_mode = "multi_pairwise" - # ------------------------------------------------------------ # Test 058 diff --git a/fastlane_bot/tests/test_059_PoolAndTokens.py b/fastlane_bot/tests/test_059_PoolAndTokens.py new file mode 100644 index 000000000..fd5ed8e45 --- /dev/null +++ b/fastlane_bot/tests/test_059_PoolAndTokens.py @@ -0,0 +1,72 @@ +# ------------------------------------------------------------ +# Auto generated test file `test_059_PoolAndTokens.py` +# ------------------------------------------------------------ +# source file = NBTest_059_PoolAndTokens.py +# test id = 059 +# test comment = PoolAndTokens +# ------------------------------------------------------------ + + +""" +This module contains the tests for the exchanges classes +""" +from fastlane_bot import Config +from fastlane_bot.helpers.poolandtokens import PoolAndTokens + +print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(PoolAndTokens)) + +from json import loads, dumps +from fastlane_bot.testing import * + +from fastlane_bot import __VERSION__ +require("3.0", __VERSION__) + +cfg = Config.new(config=Config.CONFIG_MAINNET) + +# ------------------------------------------------------------ +# Test 059 +# File test_059_PoolAndTokens.py +# Segment Test_PoolAndTokens_Carbon +# ------------------------------------------------------------ +def test_test_poolandtokens_carbon(): +# ------------------------------------------------------------ + with open("fastlane_bot/tests/_data/test_strategies.json", "r") as f: + for strategy in loads(f.read()): + pool_and_tokens = PoolAndTokens( + ConfigObj=cfg, + id=0, + cid="", + strategy_id=0, + last_updated="", + last_updated_block=0, + descr="", + pair_name="tkn0/tkn1", + exchange_name="", + fee=Decimal(0), + fee_float=0, + tkn0_balance=Decimal(0), + tkn1_balance=Decimal(0), + y_0=strategy["orders"][0]["y"], + z_0=strategy["orders"][0]["z"], + A_0=strategy["orders"][0]["A"], + B_0=strategy["orders"][0]["B"], + y_1=strategy["orders"][1]["y"], + z_1=strategy["orders"][1]["z"], + A_1=strategy["orders"][1]["A"], + B_1=strategy["orders"][1]["B"], + sqrt_price_q96=Decimal(0), + tick=0, + tick_spacing=0, + liquidity=Decimal(0), + address="", + anchor="", + tkn0="", + tkn1="", + tkn0_address="", + tkn1_address="", + tkn0_decimals=strategy["tkn0_decimals"], + tkn1_decimals=strategy["tkn1_decimals"], + ) + cpc_dict = pool_and_tokens._carbon_to_cpc_dict() + for key in ["pm_within_range", "no_limit_orders", "prices_overlap"]: + assert cpc_dict[key] == strategy[key], f"expected {key}={strategy[key]}, got {key}={cpc_dict[key]}: {dumps(strategy, indent=4)}" diff --git a/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py b/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py index 33b0fd1c3..c80f24d9a 100644 --- a/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py +++ b/fastlane_bot/tests/test_060_TestRoutehandlerCarbonPrecision.py @@ -166,29 +166,7 @@ def init_bot(mgr: Manager) -> CarbonBot: def test_test_precision_using_all_tokens_in_carbon(): # ------------------------------------------------------------ - # + - arb_mode = "multi" - - arb_finder = bot._get_arb_finder(arb_mode) - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - - r = [arb for arb in r if len(arb[2]) >= 2] - r.sort(key=lambda x: x[0], reverse=True) - print(f"number of arbs found = {len(r)}") - - - # - - - def calculate_trade_outputs(tx_route_handler: TxRouteHandler, - trade_instructions: List[TradeInstruction] - ) -> List[TradeInstruction]: + def calculate_trade_outputs(tx_route_handler: TxRouteHandler, trade_instructions: List[TradeInstruction]): """ Refactored calculate trade outputs. @@ -196,11 +174,6 @@ def calculate_trade_outputs(tx_route_handler: TxRouteHandler, ---------- trade_instructions: List[Dict[str, Any]] The trade instructions. - - Returns - ------- - List[Dict[str, Any]] - The trade outputs. """ next_amount_in = trade_instructions[0].amtin @@ -351,53 +324,30 @@ def calculate_trade_outputs(tx_route_handler: TxRouteHandler, next_amount_in = amount_out - return trade_instructions - - - for arb in r: - - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb - + arb_finder = bot.get_arb_finder("multi_pairwise_all", flashloan_tokens=flashloan_tokens, CCm=CCm) + + for arb_opp in arb_finder.find_arb_opps(): + src_token = arb_opp["src_token"] + trade_instructions_dic = arb_opp["trade_instructions_dic"] + # Order the trade instructions - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token - ) + ordered_trade_instructions_dct = bot._simple_ordering_by_src_token(trade_instructions_dic, src_token) # Scale the trade instructions - ordered_scaled_dcts = bot._basic_scaling( - ordered_trade_instructions_dct, best_src_token - ) + ordered_scaled_dcts = bot._basic_scaling(ordered_trade_instructions_dct, src_token) # Convert the trade instructions - ordered_trade_instructions_objects = bot._convert_trade_instructions( - ordered_scaled_dcts - ) + ordered_trade_instructions_objects = bot._convert_trade_instructions(ordered_scaled_dcts) # Create the tx route handler - tx_route_handler = TxRouteHandler( - trade_instructions=ordered_trade_instructions_objects - ) + tx_route_handler = TxRouteHandler(trade_instructions=ordered_trade_instructions_objects) # Aggregate the carbon trades - agg_trade_instructions = ( + trade_instructions = ( tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) - if bot._carbon_in_trade_route(ordered_trade_instructions_objects) + if any(trade.is_carbon for trade in ordered_trade_instructions_objects) else ordered_trade_instructions_objects ) # Calculate the trade instructions - #try: - calculated_trade_instructions = calculate_trade_outputs(tx_route_handler=tx_route_handler,trade_instructions=agg_trade_instructions) - - - - \ No newline at end of file + calculate_trade_outputs(tx_route_handler, trade_instructions) diff --git a/fastlane_bot/tests/test_061_TestWETHConversion.py b/fastlane_bot/tests/test_061_TestWETHConversion.py index aa96f81e2..59286c7c4 100644 --- a/fastlane_bot/tests/test_061_TestWETHConversion.py +++ b/fastlane_bot/tests/test_061_TestWETHConversion.py @@ -23,7 +23,6 @@ from fastlane_bot.helpers import TxRouteHandler from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 -from fastlane_bot.utils import num_format from fastlane_bot.helpers import add_wrap_or_unwrap_trades_to_route, split_carbon_trades from fastlane_bot.events.managers.manager import Manager from dataclasses import asdict @@ -172,112 +171,46 @@ def init_bot(mgr: Manager) -> CarbonBot: # ## Test_Wrap_Unwrap_Gas_Token_In_Route_Struct # + -arb_mode = "multi" - -arb_finder = bot._get_arb_finder(arb_mode) -finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, -) -r = finder.find_arbitrage() - -r = [arb for arb in r if len(arb[2]) >= 2] -r.sort(key=lambda x: x[0], reverse=True) +arb_mode = "multi_pairwise_all" # - def test_wrap_unwrap_original(): - for arb in r: + arb_finder = bot.get_arb_finder(arb_mode, flashloan_tokens=flashloan_tokens, CCm=CCm) - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb + for arb_opp in arb_finder.find_arb_opps(): + src_token = arb_opp["src_token"] + trade_instructions_dic = arb_opp["trade_instructions_dic"] # Order the trade instructions - ( - ordered_trade_instructions_dct, - tx_in_count, - ) = bot._simple_ordering_by_src_token( - best_trade_instructions_dic, best_src_token - ) + ordered_trade_instructions_dct = bot._simple_ordering_by_src_token(trade_instructions_dic, src_token) # Scale the trade instructions - ordered_scaled_dcts = bot._basic_scaling( - ordered_trade_instructions_dct, best_src_token - ) + ordered_scaled_dcts = bot._basic_scaling(ordered_trade_instructions_dct, src_token) # Convert the trade instructions - ordered_trade_instructions_objects = bot._convert_trade_instructions( - ordered_scaled_dcts - ) + ordered_trade_instructions_objects = bot._convert_trade_instructions(ordered_scaled_dcts) # Create the tx route handler - tx_route_handler = TxRouteHandler( - trade_instructions=ordered_trade_instructions_objects - ) + tx_route_handler = TxRouteHandler(trade_instructions=ordered_trade_instructions_objects) # Aggregate the carbon trades - agg_trade_instructions = ( + trade_instructions = ( tx_route_handler.aggregate_carbon_trades(ordered_trade_instructions_objects) - if bot._carbon_in_trade_route(ordered_trade_instructions_objects) + if any(trade.is_carbon for trade in ordered_trade_instructions_objects) else ordered_trade_instructions_objects ) # Calculate the trade instructions - calculated_trade_instructions = tx_route_handler.calculate_trade_outputs(trade_instructions=agg_trade_instructions) + tx_route_handler.calculate_trade_outputs(trade_instructions) # Aggregate multiple Bancor V3 trades into a single trade - calculated_trade_instructions = tx_route_handler.aggregate_bancor_v3_trades( - calculated_trade_instructions - ) - flashloan_struct = tx_route_handler.generate_flashloan_struct( - trade_instructions_objects=calculated_trade_instructions) - - # 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 - ) - - # 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.info(f"Updated best_profit after calculating exact trade numbers: {num_format(best_profit_gastkn)}") - - # Calculate the arbitrage - arb = bot.calculate_arb( - arb_mode, - best_profit_gastkn, - best_profit_usd, - flashloan_tkn_profit, - calculated_trade_instructions, - fl_token_symbol, - ) - - # Log the arbitrage - cfg.logger.info(f"calculated arb: {arb}") - - # Get the flashloan amount - flashloan_amount = int(calculated_trade_instructions[0].amtin_wei) - - # Log the flashloan amount - cfg.logger.info(f"Flashloan amount: {flashloan_amount}") + tx_route_handler.aggregate_bancor_v3_trades(trade_instructions) + flashloan_struct = tx_route_handler.generate_flashloan_struct(trade_instructions) - split_trades = split_carbon_trades(cfg, calculated_trade_instructions) + split_trades = split_carbon_trades(cfg, trade_instructions) # Encode the trade instructions - encoded_trade_instructions = tx_route_handler.custom_data_encoder( + tx_route_handler.custom_data_encoder( split_trades ) @@ -288,12 +221,12 @@ def test_wrap_unwrap_original(): route_struct = [ asdict(rs) for rs in tx_route_handler.get_route_structs( - encoded_trade_instructions, deadline + split_trades, deadline ) ] # Check if the result is None - wrap_route_struct = add_wrap_or_unwrap_trades_to_route(cfg, flashloan_struct, route_struct, calculated_trade_instructions) + wrap_route_struct = add_wrap_or_unwrap_trades_to_route(cfg, flashloan_struct, route_struct, trade_instructions) assert flashloan_struct[0]["sourceTokens"][0] == wrap_route_struct[0]["sourceToken"] assert flashloan_struct[0]["sourceTokens"][0] == wrap_route_struct[-1]["targetToken"] diff --git a/fastlane_bot/tests/test_063_TestBancorPOLMode.py b/fastlane_bot/tests/test_063_TestBancorPOLMode.py index 97ade2788..c94e7d120 100644 --- a/fastlane_bot/tests/test_063_TestBancorPOLMode.py +++ b/fastlane_bot/tests/test_063_TestBancorPOLMode.py @@ -128,8 +128,6 @@ def init_bot(mgr: Manager) -> CarbonBot: CCm = bot.get_curves() pools = db.get_pool_data_with_tokens() -arb_mode = "multi_pairwise_pol" - # ------------------------------------------------------------ # Test 063 @@ -142,18 +140,6 @@ def test_test_min_profit(): assert(cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN <= 0.0001), f"[NBTest 063 TestMultiPairwisePOLMode], default_min_profit_gas_token must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN}" -# ------------------------------------------------------------ -# Test 063 -# File test_063_TestBancorPOLMode.py -# Segment Test_get_arb_finder -# ------------------------------------------------------------ -def test_test_get_arb_finder(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("multi_pairwise_pol") - assert arb_finder.__name__ == "FindArbitrageMultiPairwisePol", f"[NBTest 063 TestMultiPairwisePOLMode] Expected arb_finder class name name = FindArbitrageMultiPairwisePol, found {arb_finder.__name__}" - - # ------------------------------------------------------------ # Test 063 # File test_063_TestBancorPOLMode.py @@ -162,21 +148,10 @@ def test_test_get_arb_finder(): def test_test_combos_and_tokens(): # ------------------------------------------------------------ - arb_finder = bot._get_arb_finder("multi_pairwise_pol") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_TOKENS, - ConfigObj=bot.ConfigObj, - ) - 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)}" + arb_finder = bot.get_arb_finder("multi_pairwise_pol", flashloan_tokens=flashloan_tokens, CCm=CCm) + combos = arb_finder.find_combos() assert ('0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2') in combos or ('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C') in combos, f"[NBTest 063 TestMultiPairwisePOLMode] Expected BNT/WETH or WETH/BNT in combos" - assert len(all_tokens) >= 73, f"[NBTest 063 TestMultiPairwisePOLMode] Using wrong dataset, expected at least 73 tokens, found {len(all_tokens)}" - assert len(combos) >= 146, f"[NBTest 063 TestMultiPairwisePOLMode] Using wrong dataset, expected at least 146 combos, found {len(combos)}" + assert len(combos) >= 73, f"[NBTest 063 TestMultiPairwisePOLMode] Using wrong dataset, expected at least 73 combos, found {len(combos)}" # ------------------------------------------------------------ @@ -188,31 +163,16 @@ def test_test_expected_output(): # ------------------------------------------------------------ # + - arb_finder = bot._get_arb_finder("multi_pairwise_pol") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - - r = finder.find_arbitrage() + arb_finder = bot.get_arb_finder("multi_pairwise_pol", flashloan_tokens=flashloan_tokens, CCm=CCm) + arb_opps = arb_finder.find_arb_opps() multi_carbon_count = 0 carbon_wrong_direction_count = 0 - for arb in r: - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb - if len(best_trade_instructions_dic) > 2: + for arb_opp in arb_opps: + if len(arb_opp["trade_instructions_dic"]) > 2: multi_carbon_count += 1 carbon_tkn_in = None - for trade in best_trade_instructions_dic: + for trade in arb_opp["trade_instructions_dic"]: if "-" in trade["cid"]: if carbon_tkn_in is None: carbon_tkn_in = trade["tknin"] @@ -220,6 +180,6 @@ def test_test_expected_output(): if trade["tknin"] not in carbon_tkn_in: carbon_wrong_direction_count += 1 - assert len(r) >= 36, f"[NBTest 063 TestMultiPairwisePOLMode] Expected at least 27 arbs, found {len(r)}" + assert len(arb_opps) >= 18, f"[NBTest 063 TestMultiPairwisePOLMode] Expected at least 18 arb opps, found {len(arb_opps)}" assert multi_carbon_count > 0, f"[NBTest 063 TestMultiPairwisePOLMode] Not finding arbs with multiple Carbon curves." assert carbon_wrong_direction_count == 0, f"[NBTest 063 TestMultiPairwisePOLMode Mode] Expected all Carbon curves to have the same tkn in and tkn out. Mixing is currently not supported." \ No newline at end of file diff --git a/fastlane_bot/tests/test_064_TestMultiAllMode.py b/fastlane_bot/tests/test_064_TestMultiAllMode.py index 6d7ff45be..83e19410b 100644 --- a/fastlane_bot/tests/test_064_TestMultiAllMode.py +++ b/fastlane_bot/tests/test_064_TestMultiAllMode.py @@ -128,8 +128,6 @@ def init_bot(mgr: Manager) -> CarbonBot: CCm = bot.get_curves() pools = db.get_pool_data_with_tokens() -arb_mode = "multi_pairwise_all" - # ------------------------------------------------------------ # Test 064 @@ -142,18 +140,6 @@ def test_test_min_profit(): assert(cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN <= 0.0001), f"[NBTest64 TestMultiPairwiseAll Mode], default_min_profit_gas_token must be <= 0.02 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN}" -# ------------------------------------------------------------ -# Test 064 -# File test_064_TestMultiAllMode.py -# Segment Test_get_arb_finder -# ------------------------------------------------------------ -def test_test_get_arb_finder(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("multi_pairwise_all") - assert arb_finder.__name__ == "FindArbitrageMultiPairwiseAll", f"[NBTest64 TestMultiPairwiseAll Mode] Expected arb_finder class name name = FindArbitrageMultiPairwiseAll, found {arb_finder.__name__}" - - # ------------------------------------------------------------ # Test 064 # File test_064_TestMultiAllMode.py @@ -163,19 +149,8 @@ def test_test_combos_and_tokens(): # ------------------------------------------------------------ # + - arb_finder = bot._get_arb_finder("multi_pairwise_all") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_TOKENS, - ConfigObj=bot.ConfigObj, - ) - 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)}" - assert len(all_tokens) > 100, f"[NBTest64 TestMultiPairwiseAll Mode] Using wrong dataset, expected at least 100 tokens, found {len(all_tokens)}" + arb_finder = bot.get_arb_finder("multi_pairwise_all", flashloan_tokens=flashloan_tokens, CCm=CCm) + combos = arb_finder.find_combos() assert len(combos) > 1000, f"[NBTest64 TestMultiPairwiseAll Mode] Using wrong dataset, expected at least 100 combos, found {len(combos)}" # - @@ -189,30 +164,16 @@ def test_test_expected_output(): # ------------------------------------------------------------ # + - arb_finder = bot._get_arb_finder("multi_pairwise_all") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() + arb_finder = bot.get_arb_finder("multi_pairwise_all", flashloan_tokens=flashloan_tokens, CCm=CCm) + arb_opps = arb_finder.find_arb_opps() multi_carbon_count = 0 carbon_wrong_direction_count = 0 - for arb in r: - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb - if len(best_trade_instructions_dic) > 2: + for arb_opp in arb_opps: + if len(arb_opp["trade_instructions_dic"]) > 2: multi_carbon_count += 1 carbon_tkn_in = None - for trade in best_trade_instructions_dic: + for trade in arb_opp["trade_instructions_dic"]: if "-" in trade["cid"]: if carbon_tkn_in is None: carbon_tkn_in = trade["tknin"] @@ -222,7 +183,7 @@ def test_test_expected_output(): - assert len(r) >= 25, f"[NBTest64 TestMultiPairwiseAll Mode] Expected at least 25 arbs, found {len(r)}" + assert len(arb_opps) >= 25, f"[NBTest64 TestMultiPairwiseAll Mode] Expected at least 25 arb opps, found {len(arb_opps)}" assert multi_carbon_count > 0, f"[NBTest64 TestMultiPairwiseAll Mode] Not finding arbs with multiple Carbon curves." assert carbon_wrong_direction_count == 6, f"[NBTest64 TestMultiPairwiseAll Mode] Expected 6 Carbon curves to be in the opposite direction." # - diff --git a/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py b/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py index a003d4fdf..a07d63a92 100644 --- a/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py +++ b/fastlane_bot/tests/test_901_TestMultiTriangleModeSlow.py @@ -129,24 +129,6 @@ def init_bot(mgr: Manager) -> CarbonBot: CCm = bot.get_curves() pools = db.get_pool_data_with_tokens() -arb_mode = "multi_triangle" - - -# ------------------------------------------------------------ -# Test 901 -# File test_901_TestMultiTriangleModeSlow.py -# Segment Test_min_profit -# ------------------------------------------------------------ -def test_test_min_profit(): -# ------------------------------------------------------------ - - assert(cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN <= 0.0001), f"[TestMultiTriangleMode], default_min_profit_gas_token must be <= 0.0001 for this Notebook to run, currently set to {cfg.DEFAULT_MIN_PROFIT_GAS_TOKEN}" - - # ### Test_arb_mode_class - - arb_finder = bot._get_arb_finder("multi_triangle") - assert arb_finder.__name__ == "ArbitrageFinderTriangleMulti", f"[TestMultiTriangleMode] Expected arb_finder class name name = FindArbitrageMultiPairwise, found {arb_finder.__name__}" - # ------------------------------------------------------------ # Test 901 @@ -156,146 +138,32 @@ def test_test_min_profit(): def test_test_combos(): # ------------------------------------------------------------ - arb_finder = bot._get_arb_finder("multi_triangle") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_TOKENS, - ConfigObj=bot.ConfigObj, - ) - 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)}" - - # + - # print(len(combos)) - # for ex in exchanges: - # count = 0 - # for pool in CCm: - # if ex in pool.descr: - # count +=1 - # print(f"found {count} pools for {ex}") - # - - - # ### Test_find_arbitrage_single - - # + - arb_finder = bot._get_arb_finder("multi_triangle") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() - multi_carbon_count = 0 - for arb in r: - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb - if len(best_trade_instructions_dic) > 3: - multi_carbon_count += 1 - tkn_in = None - tkn_out = None - # Find the first Carbon Curve to establish tknin and tknout - for curve in best_trade_instructions_dic: - if "-0" in curve['cid'] or "-1" in curve['cid']: - tkn_in = curve["tknin"] - tknout = curve["tknout"] - break - for curve in best_trade_instructions_dic: - if "-0" in curve['cid'] or "-1" in curve['cid']: - if curve["tknin"] in [tkn_in, tkn_out] and curve["tknout"] in [tkn_in, tkn_out]: - assert curve["tknin"] in tkn_in, f"[TestMultiTriangleMode] Finding Carbon curves in opposite directions - not supported in this mode." - assert curve["tknout"] in tkn_out, f"[TestMultiTriangleMode] Finding Carbon curves in opposite directions - not supported in this mode." - - assert multi_carbon_count > 0, f"[TestMultiTriangleMode] Not finding arbs with multiple Carbon curves." - assert len(r) >= 58, f"[TestMultiTriangleMode] Expected at least 58 arbs, found {len(r)}" - # - - - -# ------------------------------------------------------------ -# Test 901 -# File test_901_TestMultiTriangleModeSlow.py -# Segment Test Triangle Single -# ------------------------------------------------------------ -def test_test_triangle_single(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("triangle") - assert arb_finder.__name__ == "ArbitrageFinderTriangleSingle", f"[TestMultiTriangleMode] Expected arb_finder class name name = ArbitrageFinderTriangleSingle, found {arb_finder.__name__}" - - -# ------------------------------------------------------------ -# Test 901 -# File test_901_TestMultiTriangleModeSlow.py -# Segment Test_combos_triangle_single -# ------------------------------------------------------------ -def test_test_combos_triangle_single(): -# ------------------------------------------------------------ - - arb_finder = bot._get_arb_finder("triangle") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_TOKENS, - ConfigObj=bot.ConfigObj, - ) - combos = finder.get_combos(flashloan_tokens=flashloan_tokens, CCm=CCm, arb_mode="multi_triangle") + arb_finder = bot.get_arb_finder("multi_triangle", flashloan_tokens=flashloan_tokens, CCm=CCm) + combos = arb_finder.get_combos() assert len(combos) >= 1225, f"[TestMultiTriangleMode] Using wrong dataset, expected at least 1225 combos, found {len(combos)}" - -# ------------------------------------------------------------ -# Test 901 -# File test_901_TestMultiTriangleModeSlow.py -# Segment Test_Find_Arbitrage_Single -# ------------------------------------------------------------ -def test_test_find_arbitrage_single(): -# ------------------------------------------------------------ - # + - arb_finder = bot._get_arb_finder("triangle") - finder = arb_finder( - flashloan_tokens=flashloan_tokens, - CCm=CCm, - mode="bothin", - result=arb_finder.AO_CANDIDATES, - ConfigObj=bot.ConfigObj, - ) - r = finder.find_arbitrage() + arb_finder = bot.get_arb_finder("multi_triangle", flashloan_tokens=flashloan_tokens, CCm=CCm) + arb_opps = arb_finder.find_arb_opps() + multi_carbon_count = 0 - for arb in r: - ( - best_profit, - best_trade_instructions_df, - best_trade_instructions_dic, - best_src_token, - best_trade_instructions, - ) = arb - if len(best_trade_instructions_dic) > 3: + for arb_opp in arb_opps: + if len(arb_opp["trade_instructions_dic"]) > 3: multi_carbon_count += 1 tkn_in = None tkn_out = None # Find the first Carbon Curve to establish tknin and tknout - for curve in best_trade_instructions_dic: + for curve in arb_opp["trade_instructions_dic"]: if "-0" in curve['cid'] or "-1" in curve['cid']: tkn_in = curve["tknin"] tknout = curve["tknout"] break - for curve in best_trade_instructions_dic: + for curve in arb_opp["trade_instructions_dic"]: if "-0" in curve['cid'] or "-1" in curve['cid']: if curve["tknin"] in [tkn_in, tkn_out] and curve["tknout"] in [tkn_in, tkn_out]: assert curve["tknin"] in tkn_in, f"[TestMultiTriangleMode] Finding Carbon curves in opposite directions - not supported in this mode." assert curve["tknout"] in tkn_out, f"[TestMultiTriangleMode] Finding Carbon curves in opposite directions - not supported in this mode." - assert multi_carbon_count == 0, f"[TestMultiTriangleMode] Expected 0 arbs with multiple Carbon curves for Triangle Single mode, found {multi_carbon_count}." - assert len(r) >= 58, f"[TestMultiTriangleMode] Expected at least 58 arbs, found {len(r)}" + assert multi_carbon_count > 0, f"[TestMultiTriangleMode] Not finding arbs with multiple Carbon curves." + assert len(arb_opps) >= 58, f"[TestMultiTriangleMode] Expected at least 58 arb opps, found {len(arb_opps)}" # - - - \ No newline at end of file diff --git a/fastlane_bot/tests/test_903_FlashloanTokens.py b/fastlane_bot/tests/test_903_FlashloanTokens.py index 9176782ab..b1dce1345 100644 --- a/fastlane_bot/tests/test_903_FlashloanTokens.py +++ b/fastlane_bot/tests/test_903_FlashloanTokens.py @@ -7,15 +7,13 @@ # ------------------------------------------------------------ - """ -This module contains the tests which ensure the the flashloan_tokens parameter is respected when using the b3_two_hop and bancor_v3 arb modes. +This module contains the tests the `b3_two_hop` arb-mode with the `flashloan_tokens` parameter. """ from fastlane_bot import Bot from fastlane_bot.tools.cpc import ConstantProductCurve as CPC from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 -import subprocess, os, sys -import pytest +import subprocess, os print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) @@ -28,15 +26,10 @@ require("3.0", __VERSION__) - - def find_main_py(): # Start at the directory of the current script cwd = os.path.abspath(os.path.join(os.getcwd())) - with open("log.txt", "w") as f: - f.write(f"Searching for main.py in {cwd}") - print(f"Searching for main.py in {cwd}") while True: # Check if main.py exists in the current directory @@ -53,18 +46,18 @@ def find_main_py(): cwd = new_cwd -def run_command(mode): +# ------------------------------------------------------------ +# Test 903 +# File test_903_FlashloanTokens.py +# Segment Test b3_two_hop with flashloan tokens +# ------------------------------------------------------------ +def test_test_b3_two_hop_with_flashloan_tokens(): +# ------------------------------------------------------------ - # Find the correct path to main.py - main_script_path = find_main_py() - print(f"Found main.py in {main_script_path}") - main_script_path = main_script_path + "/main.py" - - # Run the command cmd = [ "python", - main_script_path, - f"--arb_mode={mode}", + find_main_py() + "/main.py", + f"--arb_mode=b3_two_hop", "--default_min_profit_gas_token=60", "--limit_bancor3_flashloan_tokens=True", "--alchemy_max_block_fetch=5", @@ -73,18 +66,5 @@ def run_command(mode): "--blockchain=ethereum" ] - expected_log_line = "limiting flashloan_tokens to [" result = subprocess.run(cmd, text=True, capture_output=True, check=True) - assert expected_log_line in result.stderr, result.stderr - - -# ------------------------------------------------------------ -# Test 903 -# File test_903_FlashloanTokens.py -# Segment Test Flashloan Tokens b3_two_hop -# ------------------------------------------------------------ -def test_test_flashloan_tokens_b3_two_hop(): -# ------------------------------------------------------------ - - # + is_executing=true - run_command("b3_two_hop") \ No newline at end of file + assert result.returncode == 0 diff --git a/fastlane_bot/tests/test_906_TargetTokens.py b/fastlane_bot/tests/test_906_TargetTokens.py index b2651b76c..66fdbf402 100644 --- a/fastlane_bot/tests/test_906_TargetTokens.py +++ b/fastlane_bot/tests/test_906_TargetTokens.py @@ -7,15 +7,13 @@ # ------------------------------------------------------------ - """ -This module contains the tests which ensure the target_tokens parameter is respected. +This module contains the tests the `b3_two_hop` arb-mode with the `target_tokens` parameter. """ -from fastlane_bot import Bot +from fastlane_bot import Bot, ConfigNetwork from fastlane_bot.tools.cpc import ConstantProductCurve as CPC from fastlane_bot.events.exchanges import UniswapV2, UniswapV3, CarbonV1, BancorV3 -import subprocess, os, sys -import pytest +import subprocess, os print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(CPC)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(Bot)) print("{0.__name__} v{0.__VERSION__} ({0.__DATE__})".format(UniswapV2)) @@ -28,17 +26,10 @@ require("3.0", __VERSION__) - -from fastlane_bot.tools.cpc import T - - def find_main_py(): # Start at the directory of the current script cwd = os.path.abspath(os.path.join(os.getcwd())) - with open("log.txt", "w") as f: - f.write(f"Searching for main.py in {cwd}") - print(f"Searching for main.py in {cwd}") while True: # Check if main.py exists in the current directory @@ -55,37 +46,24 @@ def find_main_py(): cwd = new_cwd -def run_command(mode): +# ------------------------------------------------------------ +# Test 906 +# File test_906_TargetTokens.py +# Segment Test b3_two_hop with target tokens +# ------------------------------------------------------------ +def test_test_b3_two_hop_with_target_tokens(): +# ------------------------------------------------------------ - # Find the correct path to main.py - main_script_path = find_main_py() - print(f"Found main.py in {main_script_path}") - main_script_path = main_script_path + "/main.py" - - # Run the command cmd = [ "python", - main_script_path, - f"--arb_mode={mode}", - # "--use_cached_events=True", + find_main_py() + "/main.py", + f"--arb_mode=b3_two_hop", "--alchemy_max_block_fetch=5", "--logging_path=fastlane_bot/data/", "--timeout=120", - f"--target_tokens={T.WETH},{T.DAI}", + f"--target_tokens={ConfigNetwork.WETH_ADDRESS},{ConfigNetwork.DAI_ADDRESS}", "--blockchain=ethereum" ] - expected_log_line = "Limiting pools by target_tokens. Removed " result = subprocess.run(cmd, text=True, capture_output=True, check=True) - assert expected_log_line in result.stderr, result.stderr - - -# ------------------------------------------------------------ -# Test 906 -# File test_906_TargetTokens.py -# Segment Test Flashloan Tokens b3_two_hop -# ------------------------------------------------------------ -def test_test_flashloan_tokens_b3_two_hop(): -# ------------------------------------------------------------ - - run_command("single") \ No newline at end of file + assert result.returncode == 0 diff --git a/fastlane_bot/tests_on_hold/test_907_RuntimeParameters.py b/fastlane_bot/tests_on_hold/test_907_RuntimeParameters.py index f36d97192..15bee3d0a 100644 --- a/fastlane_bot/tests_on_hold/test_907_RuntimeParameters.py +++ b/fastlane_bot/tests_on_hold/test_907_RuntimeParameters.py @@ -33,8 +33,6 @@ def __init__(self): self.logging_path = "" self.loglevel = "INFO" self.use_cached_events = 'False' - self.run_data_validator = 'False' - self.randomizer = "3" self.limit_bancor3_flashloan_tokens = 'True' self.default_min_profit_gas_token = "0.01" self.timeout = 1 @@ -57,9 +55,7 @@ def __init__(self): arb_mode_happy_path_options = [ - "single", - "multi", - "triangle", + "multi_pairwise_all", "multi_triangle", "b3_two_hop", ] @@ -82,7 +78,6 @@ def __init__(self): "carbon_v1,bancor_v3,bancor_v2", "carbon_v1,bancor_v3", ] -randomizer_happy_path_options = [1, '2'] blockchain_happy_path_options = ["ethereum", "coinbase_base"] @pytest.mark.parametrize("arb_mode", arb_mode_happy_path_options) @@ -173,17 +168,6 @@ def test_timeout(timeout, mock_args, capsys): output = f.read() assert f"timeout: {timeout}" in output -@pytest.mark.parametrize("randomizer", randomizer_happy_path_options) -def test_randomizer(randomizer, mock_args, capsys): - mock_args.randomizer = randomizer - with open('output.txt', 'w') as f: - with redirect_stderr(f): - main(mock_args) - # read the console output - with open('output.txt', 'r') as f: - output = f.read() - assert f"randomizer: {randomizer}" in output - @pytest.mark.parametrize("blockchain", blockchain_happy_path_options) def test_blockchain(blockchain, mock_args, capsys): mock_args.blockchain = blockchain diff --git a/fastlane_bot/utils.py b/fastlane_bot/utils.py index bfae0fe7b..b2ad1406d 100644 --- a/fastlane_bot/utils.py +++ b/fastlane_bot/utils.py @@ -1,193 +1,21 @@ """ Various utility functions -NOTE: Those functions would more naturally fit in `helpers` -TODO-MIKE, TODO-KEVIN: check how hard this is - --- (c) Copyright Bprotocol foundation 2023-24. All rights reserved. Licensed under MIT. """ -import glob -import math -import os.path -from dataclasses import dataclass - +from glob import glob +from os.path import join +from decimal import Decimal -def safe_int(value: int or float) -> int: - assert value == int(value), f"non-integer `float` value {value}" - return int(value) +def safe_int(value) -> int: + int_value = int(value) + assert value == int_value, f"non-integer `float` value {value}" + return int_value -def num_format(value: int or float) -> str: - try: - return "{0:.4f}".format(value) - except Exception: - return str(value) - - -@dataclass -class EncodedOrder: - """ - a single curve as encoded by the SDK - - :token: token address - :y: number of token wei to sell on the curve - :z: curve capacity in number of token wei - :A: curve parameter A, multiplied by 2**48, encoded - :B: curve parameter B, multiplied by 2**48, encoded - ---- - :A_: curve parameter A in proper units - :B_: curve parameter B in proper units - :p_start: start token wei price of the order (in dy/dx) - :p_end: end token wei price of the order (in dy/dx) - """ - - token: str - y: int - z: int - A: int - B: int - - ONE = 2**48 - - @classmethod - def from_sdk(cls, token, order): - return cls(token=token, **order) - - @property - def descr(self): - s = self - return f"selling {s.token} @ ({1 / s.p_start}..{1 / s.p_end}) [TKNwei] per {s.token}wei" - - def __getitem__(self, item): - return getattr(self, item) - - @classmethod - def decodeFloat(cls, value): - """undoes the mantisse/exponent encoding in A,B""" - return (value % cls.ONE) << (value // cls.ONE) - - @classmethod - def decodeRate(cls, value): - return (value / cls.ONE) ** 2 - - @classmethod - def decode(cls, value): - """decodes A,B to float""" - return cls.decodeFloat(int(value)) / cls.ONE - - @staticmethod - def bitLength(value): - "minimal number of bits needed to represent the value" - return len(bin(value).lstrip("0b")) if value > 0 else 0 - - @classmethod - def encodeRate(cls, value): - "encodes a rate float to an A,B (using cls.ONE for scaling)" - data = int(math.sqrt(value) * cls.ONE) - length = cls.bitLength(data // cls.ONE) - return (data >> length) << length - - @classmethod - def encodeFloat(cls, value): - "encodes a long int value as mantisse/exponent into a shorter integer" - exponent = cls.bitLength(value // cls.ONE) - mantissa = value >> exponent - return mantissa | (exponent * cls.ONE) - - @classmethod - def encode(cls, y, p_start_hi, p_end_lo, p_marg=None, z=None, token=None): - """ - alternative constructor: creates a new encoded order from the given parameters - - :y: number of token wei currently available to sell on the curve (loading) - :p_start_hi: start token wei price of the order (in dy/dx; highest) - :p_end_lo: end token wei price of the order (in dy/dx; lowest) - :p_marg: marginal token wei price of the order (in dy/dx)* - :z: curve capacity in number of token wei* - - *at most one of p_marg and z can be given; if neither is given, - z is assumed to be equal to y - """ - if token is None: - token = "TKN" - if p_marg is not None and z is not None: - raise ValueError("at most one of p_marg and z can be given") - if z is None: - if p_marg is not None and p_marg >= p_start_hi or p_marg is None: - z = y - else: - z = y * (p_start_hi - p_end_lo) // (p_marg - p_start_hi) - return cls( - y=int(y), - z=int(z), - A=cls.encodeFloat(cls.encodeRate(p_start_hi) - cls.encodeRate(p_end_lo)), - B=cls.encodeFloat(cls.encodeRate(p_end_lo)), - token=token, - ) - - @classmethod - def encode_yzAB(cls, y, z, A, B, token=None): - """ - encode A,B into the SDK format - - :y: number of token wei currently available to sell on the curve (loading; as int) - :z: curve capacity in number of token wei (as int) - :A: curve parameter A (as float) - :B: curve parameter B (as float) - """ - return cls( - y=int(y), - z=int(z), - A=cls.encodeFloat(A), - B=cls.encodeFloat(B), - token=str(token), - ) - - @dataclass - class DecodedOrder: - """ - a single curve with the values of A,B decoded and as floats - """ - - y: int - z: int - A: float - B: float - - @property - def decoded(self): - """ - returns a the order with A, B decoded as floats - """ - return self.DecodedOrder(y=self.y, z=self.z, A=self.A_, B=self.B_) - - @property - def A_(self): - return self.decode(self.A) - - @property - def B_(self): - return self.decode(self.B) - - @property - def p_end(self): - return self.B_ * self.B_ - - @property - def p_start(self): - return (self.B_ + self.A_) ** 2 - - @property - def p_marg(self): - A = self.decodeFloat(int(self.A)) - B = self.decodeFloat(int(self.B)) - if self.y == self.z: - return self.decodeRate(B + A) - else: - return self.decodeRate(B + A * self.y/self.z) def find_latest_timestamped_folder(logging_path=None): """ @@ -200,11 +28,130 @@ def find_latest_timestamped_folder(logging_path=None): str: Path to the latest timestamped folder, or None if no folder is found. """ search_path = logging_path if logging_path else "." - search_path = os.path.join(search_path, "logs/*") - list_of_folders = glob.glob(search_path) + search_path = join(search_path, "logs/*") + list_of_folders = glob(search_path) if not list_of_folders: return None list_of_folders.sort(reverse=True) # Sort the folders in descending order return list_of_folders[0] # The first one is the latest + + +ONE = 2 ** 48 + +MAX_UINT128 = 2 ** 128 - 1 +MAX_UINT256 = 2 ** 256 - 1 + +def check(val, max): assert 0 <= val <= max; return val + +def uint128(n): return check(n, MAX_UINT128) +def add(a, b): return check(a + b, MAX_UINT256) +def sub(a, b): return check(a - b, MAX_UINT256) +def mul(a, b): return check(a * b, MAX_UINT256) +def mulDivF(a, b, c): return check(a * b // c, MAX_UINT256) +def mulDivC(a, b, c): return check((a * b + c - 1) // c, MAX_UINT256) + +def bitLength(value: int) -> int: + return len(bin(value).lstrip('0b')) if value > 0 else 0 + +def encodeRate(value: Decimal) -> int: + data = int(value.sqrt() * ONE) + length = bitLength(data // ONE) + return (data >> length) << length + +def decodeRate(value: Decimal) -> Decimal: + return (value / ONE) ** 2 + +def encodeFloat(value: int) -> int: + exponent = bitLength(value // ONE) + mantissa = value >> exponent + return mantissa | (exponent * ONE) + +def decodeFloat(value: int) -> int: + return (value % ONE) << (value // ONE) + +def encodeOrder(order: dict) -> dict: + y = int(order['liquidity']) + L = encodeRate(Decimal(order['lowestRate'])) + H = encodeRate(Decimal(order['highestRate'])) + M = encodeRate(Decimal(order['marginalRate'])) + return { + 'y' : y, + 'z' : y if H == M else y * (H - L) // (M - L), + 'A' : encodeFloat(H - L), + 'B' : encodeFloat(L), + } + +def decodeOrder(order: dict) -> dict: + y = Decimal(order['y']) + z = Decimal(order['z']) + A = Decimal(decodeFloat(order['A'])) + B = Decimal(decodeFloat(order['B'])) + return { + 'liquidity' : y, + 'lowestRate' : decodeRate(B), + 'highestRate' : decodeRate(B + A), + 'marginalRate' : decodeRate(B + A if y == z else B + A * y / z), + } + +# +# x * (A * y + B * z) ^ 2 +# --------------------------------- +# A * x * (A * y + B * z) + z ^ 2 +# +def tradeBySourceAmountFunc(x, y, z, A, B): + if (A == 0): + return mulDivF(x, mul(B, B), mul(ONE, ONE)) + + temp1 = mul(z, ONE) + temp2 = add(mul(y, A), mul(z, B)) + temp3 = mul(temp2, x) + + factor1 = mulDivC(temp1, temp1, MAX_UINT256) + factor2 = mulDivC(temp3, A, MAX_UINT256) + factor = max(factor1, factor2) + + temp4 = mulDivC(temp1, temp1, factor) + temp5 = mulDivC(temp3, A, factor) + if temp4 + temp5 <= MAX_UINT256: + return mulDivF(temp2, temp3 // factor, temp4 + temp5) + return temp2 // add(A, mulDivC(temp1, temp1, temp3)) + +# +# x * z ^ 2 +# ------------------------------------------- +# (A * y + B * z) * (A * y + B * z - A * x) +# +def tradeByTargetAmountFunc(x, y, z, A, B): + if (A == 0): + return mulDivC(x, mul(ONE, ONE), mul(B, B)) + + temp1 = mul(z, ONE) + temp2 = add(mul(y, A), mul(z, B)) + temp3 = sub(temp2, mul(x, A)) + + factor1 = mulDivC(temp1, temp1, MAX_UINT256) + factor2 = mulDivC(temp2, temp3, MAX_UINT256) + factor = max(factor1, factor2) + + temp4 = mulDivC(temp1, temp1, factor) + temp5 = mulDivF(temp2, temp3, factor) + return mulDivC(x, temp4, temp5) + +def tradeFunc(amount, order, func, fallback): + x = amount + y = order['y'] + z = order['z'] + A = decodeFloat(order['A']) + B = decodeFloat(order['B']) + try: + return uint128(func(x, y, z, A, B)) + except: + return fallback + +def tradeBySourceAmount(amount: int, order: dict) -> int: + return tradeFunc(amount, order, tradeBySourceAmountFunc, 0) + +def tradeByTargetAmount(amount: int, order: dict) -> int: + return tradeFunc(amount, order, tradeByTargetAmountFunc, MAX_UINT128) diff --git a/main.py b/main.py index 26351b73a..7e1a06679 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,6 @@ from fastlane_bot.exceptions import ReadOnlyException, FlashloanUnavailableException from fastlane_bot.events.version_utils import check_version_requirements from fastlane_bot.pool_finder import PoolFinder -from fastlane_bot.tools.cpc import T check_version_requirements(required_version="6.11.0", package_name="web3") @@ -60,6 +59,18 @@ from run_blockchain_terraformer import terraform_blockchain import argparse +from fastlane_bot import ConfigNetwork +default_flashloan_tokens = [ + ConfigNetwork.LINK_ADDRESS, + ConfigNetwork.ETH_ADDRESS, + ConfigNetwork.BNT_ADDRESS, + ConfigNetwork.WBTC_ADDRESS, + ConfigNetwork.DAI_ADDRESS, + ConfigNetwork.USDC_ADDRESS, + ConfigNetwork.USDT_ADDRESS, + ConfigNetwork.WETH_ADDRESS, +] + load_dotenv() @@ -84,8 +95,6 @@ def int_or_none(x): "alchemy_max_block_fetch": int, "reorg_delay": int, "use_cached_events": is_true, - "run_data_validator": is_true, - "randomizer": int, "limit_bancor3_flashloan_tokens": is_true, "timeout": int_or_none, "replay_from_block": int_or_none, @@ -214,8 +223,6 @@ def main(args: argparse.Namespace) -> None: polling_interval: {args.polling_interval} reorg_delay: {args.reorg_delay} use_cached_events: {args.use_cached_events} - run_data_validator: {args.run_data_validator} - randomizer: {args.randomizer} limit_bancor3_flashloan_tokens: {args.limit_bancor3_flashloan_tokens} timeout: {args.timeout} replay_from_block: {args.replay_from_block} @@ -434,7 +441,7 @@ def run(mgr, args, tenderly_uri=None) -> None: bot = init_bot(mgr) if args.use_specific_exchange_for_target_tokens is not None: - target_tokens = bot.get_tokens_in_exchange( + target_tokens = bot.db.get_tokens_from_exchange( exchange_name=args.use_specific_exchange_for_target_tokens ) mgr.cfg.logger.info( @@ -449,12 +456,8 @@ def run(mgr, args, tenderly_uri=None) -> None: arb_mode=args.arb_mode, bot=bot, flashloan_tokens=args.flashloan_tokens, - randomizer=args.randomizer, - run_data_validator=args.run_data_validator, target_tokens=args.target_tokens, - loop_idx=loop_idx, logging_path=args.logging_path, - replay_from_block=replay_from_block, tenderly_uri=tenderly_uri, mgr=mgr, forked_from_block=forked_from_block, @@ -585,20 +588,17 @@ def run(mgr, args, tenderly_uri=None) -> None: default="multi_pairwise_all", help="See arb_mode in bot.py", choices=[ - "single", - "multi", - "triangle", "multi_triangle", + "multi_triangle_complete", "b3_two_hop", "multi_pairwise_pol", - "multi_pairwise_all" + "multi_pairwise_all", ], ) parser.add_argument( "--flashloan_tokens", - default=f"{T.LINK},{T.NATIVE_ETH},{T.BNT},{T.WBTC},{T.DAI},{T.USDC},{T.USDT},{T.WETH}", - help="The --flashloan_tokens flag refers to those token denominations which the bot can take " - "a flash loan in.", + default=",".join(default_flashloan_tokens), + help="Tokens in which the bot can take a flash-loan", ) parser.add_argument( "--n_jobs", default=-1, help="Number of parallel jobs to run" @@ -637,16 +637,6 @@ def run(mgr, args, tenderly_uri=None) -> None: default='False', help="Set to True for debugging / testing. Set to False for production.", ) - parser.add_argument( - "--run_data_validator", - default='False', - help="Set to True for debugging / testing. Set to False for production.", - ) - parser.add_argument( - "--randomizer", - default="3", - help="Set to the number of arb opportunities to pick from.", - ) parser.add_argument( "--limit_bancor3_flashloan_tokens", default='True', diff --git a/pyproject.toml b/pyproject.toml index b4831a945..ec038192b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "fastlane-bot" -version = "3.2.4" +version = "4.0.0" description = "Carbon Arbitrage Bot, an open-source arbitrage protocol, allows any user to perform arbitrage between Bancor ecosystem protocols and external exchanges and redirect arbitrage profits back to the protocol." authors = ["Bancor Network "] license = "MIT" diff --git a/run_blockchain_terraformer.py b/run_blockchain_terraformer.py index e8e61a56d..2322fe8cf 100644 --- a/run_blockchain_terraformer.py +++ b/run_blockchain_terraformer.py @@ -615,10 +615,10 @@ def organize_pool_details_solidly_v2( last_updated_block = pool_data["blockNumber"] if exchange == XFAI_V0_NAME: - stable_pool = "volatile" + pool_type = "volatile" tokens = [pool_data["args"]["token"], "0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f"] # TODO Use the constant WRAPPED_GAS_TOKEN_ADDRESS for this network else: - stable_pool = "stable" if pool_data["args"]["stable"] else "volatile" + pool_type = "stable" if pool_data["args"]["stable"] else "volatile" tokens = [pool_data["args"]["token0"], pool_data["args"]["token1"]] pool_contract = async_web3.eth.contract(address=pool_address, abi=exchange_object.get_abi()) @@ -653,7 +653,7 @@ def organize_pool_details_solidly_v2( "tkn0_balance": 0, "tkn1_balance": 0, "exchange": exchange, - "pool_type": stable_pool, + "pool_type": pool_type, } return {**pool_info, **token_info} diff --git a/scan_log_errors.py b/scan_log_errors.py deleted file mode 100644 index 5fd520ff8..000000000 --- a/scan_log_errors.py +++ /dev/null @@ -1,173 +0,0 @@ -import os -import re -import time -from datetime import datetime -from datetime import timedelta - -import click - - -def is_valid_timestamp_dir(name, time_format): - try: - datetime.strptime(name, time_format) - return True - except ValueError: - return False - - -def contains_only_one_specific_word(file_path, target_word, other_words): - with open(file_path, "r") as file: - file_contents = file.read() - - # Define a regex pattern with word boundaries for the target word - target_word_pattern = r"\b" + re.escape(target_word) + r"\b" - target_word_count = len(re.findall(target_word_pattern, file_contents)) - if target_word_count == 0: - return False - - # Check for other words - for word in other_words: - if word != target_word: - # Define a regex pattern with word boundaries for each other word - word_pattern = r"\b" + re.escape(word) + r"\b" - if re.search(word_pattern, file_contents): - return False - - return True - - -def find_latest_log_with_string( - logs_dir, search_string, non_search_patterns, time_format="%Y%m%d-%H%M%S" -): - latest_time = None - latest_folder = None - - for folder_name in os.listdir(logs_dir): - folder_path = os.path.join(logs_dir, folder_name) - if os.path.isdir(folder_path) and is_valid_timestamp_dir( - folder_name, time_format - ): - log_file_path = os.path.join(folder_path, "bot.log") - if os.path.exists(log_file_path): - is_match = contains_only_one_specific_word( - log_file_path, search_string, non_search_patterns - ) - if is_match: - folder_time = datetime.strptime(folder_name, time_format) - if not latest_time or folder_time > latest_time: - latest_time = folder_time - latest_folder = folder_path - - return latest_folder - - -def read_log_file(file_path): - with open(file_path, "r") as file: - return file.read() - - -def scan_logs_for_opportunity( - logs, search_pattern, max_minutes, latest_opportunity_timestamp=None -): - time_pattern = r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}),\d{3}" - search_regex = re.compile(time_pattern + r".*" + search_pattern) - error_detected = True - - for line in logs.split("\n"): - match = search_regex.search(line) - if match: - last_timestamp = match.group(1) - if ( - latest_opportunity_timestamp - and datetime.strptime(last_timestamp, "%Y-%m-%d %H:%M:%S") - > latest_opportunity_timestamp - ): - error_detected = False - print(f"Opportunity found at {last_timestamp}") - break - - if error_detected: - raise ValueError( - f"Opportunity not found within {max_minutes} minutes interval." - ) - - -@click.command() -@click.option("--logs_directory", default="logs", type=str) -@click.option("--interval", default=30, type=int) -@click.option("--search_pattern", default="multi_pairwise_all", type=str) -@click.option("--max_minutes", default=10, type=int) -def main(logs_directory, interval, search_pattern, max_minutes): - - all_patterns = [ - "single", - "multi", - "triangle", - "multi_triangle", - "bancor_v3", - "b3_two_hop", - "multi_pairwise_pol", - "multi_pairwise_bal", - "multi_pairwise_all", - ] - - if search_pattern not in all_patterns: - print(f"Invalid search pattern. Valid patterns are: {all_patterns}") - raise ValueError("Invalid search pattern.") - - non_search_patterns = [ - f"arb_mode: {pattern}" for pattern in all_patterns if pattern != search_pattern - ] - - search_pattern = f"arb_mode: {search_pattern}" - - latest_folder = find_latest_log_with_string( - logs_directory, search_pattern, non_search_patterns - ) - - if latest_folder: - print(f"Latest folder containing '{search_pattern}': {latest_folder}") - else: - print("No folder found with the specified pattern.") - raise ValueError("No folder found with the specified pattern.") - - print("Starting continuous scan of log file.") - while True: - last_timestamp = datetime.now() - timedelta(minutes=max_minutes) - try: - logs = read_log_file(f"{latest_folder}/bot.log") - scan_logs_for_opportunity( - logs, r"Opportunity with profit:", max_minutes, last_timestamp - ) - print("Everything is OK...") - except ValueError as e: - next_latest_folder = find_latest_log_with_string( - logs_directory, search_pattern, non_search_patterns - ) - if next_latest_folder and next_latest_folder != latest_folder: - print( - f"Latest folder containing '{search_pattern}': {next_latest_folder}" - ) - latest_folder = next_latest_folder - continue - else: - print(e) - break - except Exception as ex: - next_latest_folder = find_latest_log_with_string( - logs_directory, search_pattern, non_search_patterns - ) - if next_latest_folder and next_latest_folder != latest_folder: - print( - f"Latest folder containing '{search_pattern}': {next_latest_folder}" - ) - latest_folder = next_latest_folder - continue - else: - print(f"An error occurred: {ex}") - break - time.sleep(interval) - - -if __name__ == "__main__": - main() diff --git a/test_get_start_block.py b/test_get_start_block.py deleted file mode 100644 index 392b2ca72..000000000 --- a/test_get_start_block.py +++ /dev/null @@ -1,74 +0,0 @@ -from fastlane_bot.events.utils import get_start_block as new_get_start_block - -def old_get_start_block( - alchemy_max_block_fetch: int, - last_block: int, - mgr: any, - reorg_delay: int, - replay_from_block: int, -) -> (int, int or None): - if replay_from_block: - return ( - replay_from_block - 1 - if last_block != 0 - else replay_from_block - reorg_delay - alchemy_max_block_fetch - ), replay_from_block - elif mgr.tenderly_fork_id: - from_block = mgr.w3_tenderly.eth.block_number - return ( - max(block["last_updated_block"] for block in mgr.pool_data) - reorg_delay - if last_block != 0 - else from_block - reorg_delay - alchemy_max_block_fetch - ), from_block - else: - current_block = mgr.web3.eth.block_number - return ( - ( - max(block["last_updated_block"] for block in mgr.pool_data) - - reorg_delay - if last_block != 0 - else current_block - reorg_delay - alchemy_max_block_fetch - ), - None - ) - -class ETH: - def __init__(self, block_number: int): - self.block_number = block_number - -class Web3: - def __init__(self, block_number: int): - self.eth = ETH(block_number) - -class MGR: - def __init__(self, tenderly_fork_id: bool, w3_tenderly_block_number: int, web3_block_number: int, pool_block_numbers: list[int or float]): - self.tenderly_fork_id = tenderly_fork_id - self.w3_tenderly = Web3(w3_tenderly_block_number) - self.web3 = Web3(web3_block_number) - self.pool_data = [{"last_updated_block": pool_block_number} for pool_block_number in pool_block_numbers] - -for alchemy_max_block_fetch in range(10): - for last_block in [0, 1]: - for reorg_delay in range(10): - for replay_from_block in range(10): - for tenderly_fork_id in [False, True]: - for w3_tenderly_block_number in range(10): - for web3_block_number in range(10): - for pool_block_numbers in [[1, 2], [1, 2.0], [1.0, 2], [1.0, 2.0]]: - print( - f"alchemy_max_block_fetch = {alchemy_max_block_fetch }\n" + - f"last_block = {last_block }\n" + - f"reorg_delay = {reorg_delay }\n" + - f"replay_from_block = {replay_from_block }\n" + - f"tenderly_fork_id = {tenderly_fork_id }\n" + - f"web3_block_number = {web3_block_number }\n" + - f"pool_block_numbers = {pool_block_numbers }\n" - ) - mgr = MGR(tenderly_fork_id, w3_tenderly_block_number, web3_block_number, pool_block_numbers) - expected = old_get_start_block(alchemy_max_block_fetch, last_block, mgr, reorg_delay, replay_from_block) - actual = new_get_start_block(alchemy_max_block_fetch, last_block, mgr, reorg_delay, replay_from_block) - for e, a in zip(expected, actual): - if type(e) is float: - assert type(a) is int and a == e, f"expected value {e} of type int, got value {a} of type {type(a)}" - else: - assert type(a) is type(e) and a == e, f"expected value {e} of type {type(e)}, got value {a} of type {type(a)}"