async def _remove_position(self, hb_id: str, token_id: str, reducePercent: Decimal):
     """
     Calls add position end point to create/increase a range position.
     :param hb_id: Internal Hummingbot id
     :param token_id: The token id of position to be increased
     :param reducePercent: The percentage of liquidity to remove from position with the specified token id
     """
     tracked_pos = self._in_flight_positions.get(hb_id)
     await tracked_pos.get_last_tx_hash()
     tracked_pos.last_status = UniswapV3PositionStatus.PENDING_REMOVE
     tracked_pos.update_last_tx_hash(None)
     try:
         result = await self._api_request("post",
                                          "eth/uniswap/v3/remove-position",
                                          {"tokenId": token_id, "reducePercent": reducePercent})
         hash = result.get("hash")
         action = "removal of" if reducePercent == Decimal("100.0") else \
                  f"{reducePercent}% reduction of liquidity for"
         self.logger().info(f"Initiated {action} of position with ID - {token_id}.")
         tracked_pos.update_last_tx_hash(hash)
         self.trigger_event(MarketEvent.RangePositionUpdated,
                            RangePositionUpdatedEvent(self.current_timestamp, tracked_pos.hb_id,
                                                      tracked_pos.last_tx_hash, tracked_pos.token_id,
                                                      tracked_pos.base_amount, tracked_pos.quote_amount,
                                                      tracked_pos.last_status.name))
     except Exception as e:
         # self.stop_tracking_position(hb_id)
         self.logger().network(
             f"Error removing range position, token_id: {token_id}, hb_id: {hb_id}",
             exc_info=True,
             app_warning_msg=str(e)
         )
         self.trigger_event(MarketEvent.RangePositionFailure,
                            RangePositionFailureEvent(self.current_timestamp, hb_id))
 async def _remove_position(self, hb_id: str, token_id: str):
     tracked_pos = self._in_flight_positions.get(hb_id)
     await tracked_pos.get_last_tx_hash()
     tracked_pos.last_status = UniswapV3PositionStatus.PENDING_REMOVE
     tracked_pos.update_last_tx_hash(None)
     try:
         result = await self._api_request("post",
                                          "eth/uniswap/v3/remove-position",
                                          {"tokenId": token_id})
         hash = result.get("hash")
         self.logger().info(
             f"Initiated removal of position with ID - {token_id}.")
         tracked_pos.update_last_tx_hash(hash)
         self.trigger_event(
             MarketEvent.RangePositionUpdated,
             RangePositionUpdatedEvent(
                 self.current_timestamp, tracked_pos.hb_id,
                 tracked_pos.last_tx_hash, tracked_pos.token_id,
                 tracked_pos.base_amount, tracked_pos.quote_amount,
                 tracked_pos.last_status.name))
     except Exception as e:
         # self.stop_tracking_position(hb_id)
         self.logger().network(
             f"Error removing range position, token_id: {token_id}, hb_id: {hb_id}",
             exc_info=True,
             app_warning_msg=str(e))
         self.trigger_event(
             MarketEvent.RangePositionFailure,
             RangePositionFailureEvent(self.current_timestamp, hb_id))
    async def _replace_position(self, hb_id: str, token_id: str,
                                trading_pair: str, fee_tier: Decimal,
                                base_amount: Decimal, quote_amount: Decimal,
                                lower_price: Decimal, upper_price: Decimal):
        """
        Calls add position end point to create a new range position.
        :param hb_id: Internal Hummingbot id
        :param trading_pair: The market trading pair of the pool
        :param fee_tier: The expected fee
        :param base_amount: The amount of base token to put into the pool
        :param lower_price: The lower bound of the price range
        :param upper_price: The upper bound of the price range
        """
        base_amount = self.quantize_order_amount(trading_pair, base_amount)
        quote_amount = self.quantize_order_amount(trading_pair, quote_amount)
        lower_price = self.quantize_order_price(trading_pair, lower_price)
        upper_price = self.quantize_order_price(trading_pair, upper_price)
        base, quote = trading_pair.split("-")
        tracked_pos = self._in_flight_positions.get(hb_id)
        await tracked_pos.get_last_tx_hash()
        tracked_pos.last_status = UniswapV3PositionStatus.PENDING_REMOVE
        tracked_pos.update_last_tx_hash(None)

        new_hb_id = f"{trading_pair}-{get_tracking_nonce()}"
        self.start_tracking_position(new_hb_id, trading_pair, fee_tier,
                                     base_amount, quote_amount, lower_price,
                                     upper_price)
        try:
            order_result = await self._api_request(
                "post", "eth/uniswap/v3/replace-position", {
                    "tokenId": token_id,
                    "token0": base,
                    "token1": quote,
                    "fee": str(fee_tier),
                    "lowerPrice": str(lower_price),
                    "upperPrice": str(upper_price),
                    "amount0": str(base_amount),
                    "amount1": str(quote_amount),
                })
            tracked_pos = self._in_flight_positions.get(new_hb_id)
            tracked_pos.token_id = order_result.get("tokenId")
            tracked_pos.gas_price = Decimal(str(order_result.get("gasPrice")))
            tracked_pos.update_last_tx_hash(order_result.get("hash"))
            self.logger().info(
                f"Initiated replacement of position with ID - {token_id}.")
        except Exception as e:
            self.stop_tracking_order(hb_id)
            self.logger().network(
                f"Error replacing range position to Uniswap with ID - {token_id} "
                f"hb_id: {hb_id}",
                exc_info=True,
                app_warning_msg=str(e))
            self.trigger_event(
                MarketEvent.RangePositionFailure,
                RangePositionFailureEvent(self.current_timestamp, hb_id))
 async def _add_position(self,
                         hb_id: str,
                         trading_pair: str,
                         fee_tier: str,
                         base_amount: Decimal,
                         quote_amount: Decimal,
                         lower_price: Decimal,
                         upper_price: Decimal,
                         token_id: int):
     """
     Calls add position end point to create/increase a range position.
     :param hb_id: Internal Hummingbot id
     :param trading_pair: The market trading pair of the pool
     :param fee_tier: The expected fee
     :param base_amount: The amount of base token to put into the pool
     :param lower_price: The lower bound of the price range
     :param upper_price: The upper bound of the price range
     :param token_id: The token id of position to be increased
     """
     base_amount = self.quantize_order_amount(trading_pair, base_amount)
     quote_amount = self.quantize_order_amount(trading_pair, quote_amount)
     lower_price = self.quantize_order_price(trading_pair, lower_price)
     upper_price = self.quantize_order_price(trading_pair, upper_price)
     base, quote = trading_pair.split("-")
     api_params = {"token0": base,
                   "token1": quote,
                   "fee": fee_tier,
                   "lowerPrice": str(lower_price),
                   "upperPrice": str(upper_price),
                   "amount0": str(base_amount),
                   "amount1": str(quote_amount),
                   "tokenId": token_id
                   }
     self.start_tracking_position(hb_id, trading_pair, fee_tier, base_amount, quote_amount,
                                  lower_price, upper_price)
     try:
         order_result = await self._api_request("post", "eth/uniswap/v3/add-position", api_params)
         tracked_pos = self._in_flight_positions[hb_id]
         tx_hash = order_result["hash"]
         tracked_pos.update_last_tx_hash(tx_hash)
         tracked_pos.gas_price = order_result.get("gasPrice")
         tracked_pos.last_status = UniswapV3PositionStatus.PENDING_CREATE
         self.logger().info(f"Adding liquidity for {trading_pair}, hb_id: {hb_id}, tx_hash: {tx_hash} "
                            f"amount: {base_amount} ({base}), "
                            f"range: {lower_price} - {upper_price}")
         self.trigger_event(
             MarketEvent.RangePositionInitiated,
             RangePositionInitiatedEvent(
                 timestamp=self.current_timestamp,
                 hb_id=hb_id,
                 tx_hash=tx_hash,
                 trading_pair=trading_pair,
                 fee_tier=fee_tier,
                 lower_price=lower_price,
                 upper_price=upper_price,
                 base_amount=base_amount,
                 quote_amount=quote_amount,
                 gas_price=tracked_pos.gas_price,
                 status=UniswapV3PositionStatus.PENDING_CREATE.name,
             )
         )
     except Exception as e:
         self.stop_tracking_order(hb_id)
         self.logger().network(
             f"Error submitting range position to Uniswap V3 for {trading_pair} "
             f"hb_id: {hb_id},"
             f"amount: {base_amount} ({base}) {quote_amount}) ({quote}), "
             f"range: {lower_price} - {upper_price}",
             exc_info=True,
             app_warning_msg=str(e)
         )
         self.trigger_event(MarketEvent.RangePositionFailure,
                            RangePositionFailureEvent(self.current_timestamp, hb_id))
 async def update_lp_order(self, update_result: Dict[str, any], tracked_pos: UniswapV3InFlightPosition):
     """
     Unlike swap orders, lp orders only stop tracking when a remove position is detected.
     """
     if update_result.get("confirmed", False):
         if update_result["receipt"].get("status", 0) == 1:
             transaction_results = await self._api_request("post",
                                                           "eth/uniswap/v3/result",
                                                           {"logs": json.dumps(update_result["receipt"]["logs"]),
                                                            "pair": tracked_pos.trading_pair})
             for result in transaction_results["info"]:
                 if result["name"] == "IncreaseLiquidity" and tracked_pos.last_status == UniswapV3PositionStatus.PENDING_CREATE:
                     token_id, amount0, amount1 = self.parse_liquidity_events(result["events"],
                                                                              transaction_results["baseDecimal"],
                                                                              transaction_results["quoteDecimal"])
                     tracked_pos.token_id = token_id
                     tracked_pos.base_amount = amount0
                     tracked_pos.quote_amount = amount1
                     tracked_pos.last_status = UniswapV3PositionStatus.OPEN
                     self.logger().info(f"Liquidity added for tokenID - {token_id}.")
                     self.trigger_event(MarketEvent.RangePositionUpdated,
                                        RangePositionUpdatedEvent(self.current_timestamp,
                                                                  tracked_pos.hb_id,
                                                                  tracked_pos.last_tx_hash,
                                                                  tracked_pos.token_id,
                                                                  tracked_pos.base_amount,
                                                                  tracked_pos.quote_amount,
                                                                  tracked_pos.last_status.name
                                                                  ))
                     self.trigger_event(MarketEvent.RangePositionCreated,
                                        RangePositionCreatedEvent(self.current_timestamp,
                                                                  tracked_pos.hb_id,
                                                                  tracked_pos.last_tx_hash,
                                                                  tracked_pos.token_id,
                                                                  tracked_pos.trading_pair,
                                                                  tracked_pos.fee_tier,
                                                                  tracked_pos.lower_price,
                                                                  tracked_pos.upper_price,
                                                                  tracked_pos.base_amount,
                                                                  tracked_pos.quote_amount,
                                                                  tracked_pos.last_status.name,
                                                                  tracked_pos.gas_price
                                                                  ))
                 elif result["name"] == "DecreaseLiquidity" and tracked_pos.last_status == UniswapV3PositionStatus.PENDING_REMOVE:
                     token_id, amount0, amount1 = self.parse_liquidity_events(result["events"],
                                                                              transaction_results["baseDecimal"],
                                                                              transaction_results["quoteDecimal"])
                     tracked_pos.token_id = token_id
                     tracked_pos.last_status = UniswapV3PositionStatus.REMOVED
                     self.logger().info(f"Liquidity decreased for tokenID - {token_id}.")
                     self.trigger_event(MarketEvent.RangePositionUpdated,
                                        RangePositionUpdatedEvent(self.current_timestamp,
                                                                  tracked_pos.hb_id,
                                                                  tracked_pos.last_tx_hash,
                                                                  tracked_pos.token_id,
                                                                  tracked_pos.base_amount,
                                                                  tracked_pos.quote_amount,
                                                                  tracked_pos.last_status.name
                                                                  ))
                     self.trigger_event(MarketEvent.RangePositionRemoved,
                                        RangePositionRemovedEvent(self.current_timestamp, tracked_pos.hb_id,
                                                                  tracked_pos.token_id))
                     self.stop_tracking_position(tracked_pos.hb_id)
                 elif result["name"] == "Collect":
                     pass
                     # not sure how to handle this at the moment
                     # token_id, amount0, amount1 = self.parse_liquidity_events(result["events"])
                     # tracked_order.update_exchange_order_id(token_id)
                     # self.logger().info(f"Liquidity removed for tokenID - {token_id}.")
         else:
             self.logger().info(
                 f"Error updating range position, token_id: {tracked_pos.token_id}, hb_id: {tracked_pos.hb_id}"
             )
             self.trigger_event(MarketEvent.RangePositionFailure,
                                RangePositionFailureEvent(self.current_timestamp, tracked_pos.hb_id))
             self.stop_tracking_position(tracked_pos.hb_id)
             tracked_pos.last_status = UniswapV3PositionStatus.FAILED