diff --git a/routers/gateway_clmm.py b/routers/gateway_clmm.py index 7229245..a94f72f 100644 --- a/routers/gateway_clmm.py +++ b/routers/gateway_clmm.py @@ -705,191 +705,209 @@ async def open_clmm_position( raise HTTPException(status_code=500, detail=f"Error opening CLMM position: {str(e)}") -# @router.post("/clmm/add") -# async def add_liquidity_to_clmm_position( -# request: CLMMAddLiquidityRequest, -# accounts_service: AccountsService = Depends(get_accounts_service), -# db_manager: AsyncDatabaseManager = Depends(get_database_manager) -# ): -# """ -# Add MORE liquidity to an EXISTING CLMM position. -# -# Example: -# connector: 'meteora' -# network: 'solana-mainnet-beta' -# position_address: '...' -# base_token_amount: 0.5 -# quote_token_amount: 50.0 -# slippage_pct: 1 -# wallet_address: (optional) -# -# Returns: -# Transaction hash -# """ -# try: -# if not await accounts_service.gateway_client.ping(): -# raise HTTPException(status_code=503, detail="Gateway service is not available") -# -# # Parse network_id -# chain, network = accounts_service.gateway_client.parse_network_id(request.network) -# -# # Get wallet address -# wallet_address = await accounts_service.gateway_client.get_wallet_address_or_default( -# chain=chain, -# wallet_address=request.wallet_address -# ) -# -# # Add liquidity to existing position -# result = await accounts_service.gateway_client.clmm_add_liquidity( -# connector=request.connector, -# network=network, -# wallet_address=wallet_address, -# position_address=request.position_address, -# base_token_amount=float(request.base_token_amount) if request.base_token_amount else None, -# quote_token_amount=float(request.quote_token_amount) if request.quote_token_amount else None, -# slippage_pct=float(request.slippage_pct) if request.slippage_pct else 1.0 -# ) -# -# transaction_hash = result.get("signature") or result.get("txHash") or result.get("hash") -# if not transaction_hash: -# raise HTTPException(status_code=500, detail="No transaction hash returned from Gateway") -# -# # Get transaction status from Gateway response -# tx_status = get_transaction_status_from_response(result) -# -# # Extract gas fee from Gateway response -# data = result.get("data", {}) -# gas_fee = data.get("fee") -# gas_token = "SOL" if chain == "solana" else "ETH" if chain == "ethereum" else None -# -# # Store ADD_LIQUIDITY event in database -# try: -# async with db_manager.get_session_context() as session: -# clmm_repo = GatewayCLMMRepository(session) -# -# # Get position to link event -# position = await clmm_repo.get_position_by_address(request.position_address) -# if position: -# event_data = { -# "position_id": position.id, -# "transaction_hash": transaction_hash, -# "event_type": "ADD_LIQUIDITY", -# "base_token_amount": float(request.base_token_amount) if request.base_token_amount else None, -# "quote_token_amount": float(request.quote_token_amount) if request.quote_token_amount else None, -# "gas_fee": float(gas_fee) if gas_fee else None, -# "gas_token": gas_token, -# "status": tx_status -# } -# await clmm_repo.create_event(event_data) -# logger.info(f"Recorded CLMM ADD_LIQUIDITY event: {transaction_hash} (status: {tx_status}, gas: {gas_fee} {gas_token})") -# except Exception as db_error: -# logger.error(f"Error recording ADD_LIQUIDITY event: {db_error}", exc_info=True) -# -# return { -# "transaction_hash": transaction_hash, -# "position_address": request.position_address, -# "status": "submitted" -# } -# -# except HTTPException: -# raise -# except ValueError as e: -# raise HTTPException(status_code=400, detail=str(e)) -# except Exception as e: -# logger.error(f"Error adding liquidity to CLMM position: {e}", exc_info=True) -# raise HTTPException(status_code=500, detail=f"Error adding liquidity to CLMM position: {str(e)}") -# -# -# @router.post("/clmm/remove") -# async def remove_liquidity_from_clmm_position( -# request: CLMMRemoveLiquidityRequest, -# accounts_service: AccountsService = Depends(get_accounts_service), -# db_manager: AsyncDatabaseManager = Depends(get_database_manager) -# ): -# """ -# Remove SOME liquidity from a CLMM position (partial removal). -# -# Example: -# connector: 'meteora' -# network: 'solana-mainnet-beta' -# position_address: '...' -# percentage: 50 -# wallet_address: (optional) -# -# Returns: -# Transaction hash -# """ -# try: -# if not await accounts_service.gateway_client.ping(): -# raise HTTPException(status_code=503, detail="Gateway service is not available") -# -# # Parse network_id -# chain, network = accounts_service.gateway_client.parse_network_id(request.network) -# -# # Get wallet address -# wallet_address = await accounts_service.gateway_client.get_wallet_address_or_default( -# chain=chain, -# wallet_address=request.wallet_address -# ) -# -# # Remove liquidity -# result = await accounts_service.gateway_client.clmm_remove_liquidity( -# connector=request.connector, -# network=network, -# wallet_address=wallet_address, -# position_address=request.position_address, -# percentage=float(request.percentage) -# ) -# -# transaction_hash = result.get("signature") or result.get("txHash") or result.get("hash") -# if not transaction_hash: -# raise HTTPException(status_code=500, detail="No transaction hash returned from Gateway") -# -# # Get transaction status from Gateway response -# tx_status = get_transaction_status_from_response(result) -# -# # Extract gas fee from Gateway response -# data = result.get("data", {}) -# gas_fee = data.get("fee") -# gas_token = "SOL" if chain == "solana" else "ETH" if chain == "ethereum" else None -# -# # Store REMOVE_LIQUIDITY event in database -# try: -# async with db_manager.get_session_context() as session: -# clmm_repo = GatewayCLMMRepository(session) -# -# # Get position to link event -# position = await clmm_repo.get_position_by_address(request.position_address) -# if position: -# event_data = { -# "position_id": position.id, -# "transaction_hash": transaction_hash, -# "event_type": "REMOVE_LIQUIDITY", -# "percentage": float(request.percentage), -# "gas_fee": float(gas_fee) if gas_fee else None, -# "gas_token": gas_token, -# "status": tx_status -# } -# await clmm_repo.create_event(event_data) -# logger.info(f"Recorded CLMM REMOVE_LIQUIDITY event: {transaction_hash} (status: {tx_status}, gas: {gas_fee} {gas_token})") -# except Exception as db_error: -# logger.error(f"Error recording REMOVE_LIQUIDITY event: {db_error}", exc_info=True) -# -# return { -# "transaction_hash": transaction_hash, -# "position_address": request.position_address, -# "percentage": float(request.percentage), -# "status": "submitted" -# } -# -# except HTTPException: -# raise -# except ValueError as e: -# raise HTTPException(status_code=400, detail=str(e)) -# except Exception as e: -# logger.error(f"Error removing liquidity from CLMM position: {e}", exc_info=True) -# raise HTTPException(status_code=500, detail=f"Error removing liquidity from CLMM position: {str(e)}") -# +@router.post("/clmm/add") +async def add_liquidity_to_clmm_position( + request: CLMMAddLiquidityRequest, + accounts_service: AccountsService = Depends(get_accounts_service), + db_manager: AsyncDatabaseManager = Depends(get_database_manager) +): + """ + Add MORE liquidity to an EXISTING CLMM position. + + Example: + connector: 'pancakeswap' + network: 'ethereum-bsc' + position_address: '6220678' + base_token_amount: 10 + quote_token_amount: 4.5 + slippage_pct: 1 + wallet_address: (optional) + + Returns: + Transaction hash + """ + try: + if not await accounts_service.gateway_client.ping(): + raise HTTPException(status_code=503, detail="Gateway service is not available") + + # Parse network_id + chain, network = accounts_service.gateway_client.parse_network_id(request.network) + + # Get wallet address + wallet_address = await accounts_service.gateway_client.get_wallet_address_or_default( + chain=chain, + wallet_address=request.wallet_address + ) + + # Add liquidity to existing position + result = await accounts_service.gateway_client.clmm_add_liquidity( + connector=request.connector, + network=network, + wallet_address=wallet_address, + position_address=request.position_address, + base_token_amount=float(request.base_token_amount) if request.base_token_amount else None, + quote_token_amount=float(request.quote_token_amount) if request.quote_token_amount else None, + slippage_pct=float(request.slippage_pct) if request.slippage_pct else 1.0 + ) + + # Extract transaction hash from Gateway response + # Gateway returns different formats: signature (Solana), transactionHash/txHash/hash (EVM chains) + transaction_hash = ( + result.get("signature") or + result.get("txHash") or + result.get("hash") or + (result.get("receipt", {}).get("transactionHash") if isinstance(result.get("receipt"), dict) else None) + ) + + if not transaction_hash: + logger.error(f"No transaction hash found in Gateway response: {result}") + raise HTTPException(status_code=500, detail=f"No transaction hash returned from Gateway. Response keys: {list(result.keys())}") + + # Get transaction status from Gateway response + tx_status = get_transaction_status_from_response(result) + + # Extract gas fee from Gateway response + data = result.get("data", {}) + gas_fee = data.get("fee") + gas_token = "SOL" if chain == "solana" else "ETH" if chain == "ethereum" else None + + # Store ADD_LIQUIDITY event in database + try: + async with db_manager.get_session_context() as session: + clmm_repo = GatewayCLMMRepository(session) + + # Get position to link event + position = await clmm_repo.get_position_by_address(request.position_address) + if position: + event_data = { + "position_id": position.id, + "transaction_hash": transaction_hash, + "event_type": "ADD_LIQUIDITY", + "base_token_amount": float(request.base_token_amount) if request.base_token_amount else None, + "quote_token_amount": float(request.quote_token_amount) if request.quote_token_amount else None, + "gas_fee": float(gas_fee) if gas_fee else None, + "gas_token": gas_token, + "status": tx_status + } + await clmm_repo.create_event(event_data) + logger.info(f"Recorded CLMM ADD_LIQUIDITY event: {transaction_hash} (status: {tx_status}, gas: {gas_fee} {gas_token})") + except Exception as db_error: + logger.error(f"Error recording ADD_LIQUIDITY event: {db_error}", exc_info=True) + + return { + "transaction_hash": transaction_hash, + "position_address": request.position_address, + "status": "submitted" + } + + except HTTPException: + raise + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.error(f"Error adding liquidity to CLMM position: {e}", exc_info=True) + raise HTTPException(status_code=500, detail=f"Error adding liquidity to CLMM position: {str(e)}") + + +@router.post("/clmm/remove") +async def remove_liquidity_from_clmm_position( + request: CLMMRemoveLiquidityRequest, + accounts_service: AccountsService = Depends(get_accounts_service), + db_manager: AsyncDatabaseManager = Depends(get_database_manager) +): + """ + Remove SOME liquidity from a CLMM position (partial removal). + + Example: + connector: 'pancakeswap' + network: 'ethereum-bsc' + position_address: '6220678' + percentage: 50 + wallet_address: (optional) + + Returns: + Transaction hash + """ + try: + if not await accounts_service.gateway_client.ping(): + raise HTTPException(status_code=503, detail="Gateway service is not available") + + # Parse network_id + chain, network = accounts_service.gateway_client.parse_network_id(request.network) + + # Get wallet address + wallet_address = await accounts_service.gateway_client.get_wallet_address_or_default( + chain=chain, + wallet_address=request.wallet_address + ) + + # Remove liquidity + result = await accounts_service.gateway_client.clmm_remove_liquidity( + connector=request.connector, + network=network, + wallet_address=wallet_address, + position_address=request.position_address, + percentage=float(request.percentage) + ) + + # Extract transaction hash from Gateway response + # Gateway returns different formats: signature (Solana), transactionHash/txHash/hash (EVM chains) + transaction_hash = ( + result.get("signature") or + result.get("txHash") or + result.get("hash") or + (result.get("receipt", {}).get("transactionHash") if isinstance(result.get("receipt"), dict) else None) + ) + + if not transaction_hash: + logger.error(f"No transaction hash found in Gateway response: {result}") + raise HTTPException(status_code=500, detail=f"No transaction hash returned from Gateway. Response keys: {list(result.keys())}") + + # Get transaction status from Gateway response + tx_status = get_transaction_status_from_response(result) + + # Extract gas fee from Gateway response + data = result.get("data", {}) + gas_fee = data.get("fee") + gas_token = get_native_gas_token(chain) + + # Store REMOVE_LIQUIDITY event in database + try: + async with db_manager.get_session_context() as session: + clmm_repo = GatewayCLMMRepository(session) + + # Get position to link event + position = await clmm_repo.get_position_by_address(request.position_address) + if position: + event_data = { + "position_id": position.id, + "transaction_hash": transaction_hash, + "event_type": "REMOVE_LIQUIDITY", + "percentage": float(request.percentage), + "gas_fee": float(gas_fee) if gas_fee else None, + "gas_token": gas_token, + "status": tx_status + } + await clmm_repo.create_event(event_data) + logger.info(f"Recorded CLMM REMOVE_LIQUIDITY event: {transaction_hash} (status: {tx_status}, gas: {gas_fee} {gas_token})") + except Exception as db_error: + logger.error(f"Error recording REMOVE_LIQUIDITY event: {db_error}", exc_info=True) + + return { + "transaction_hash": transaction_hash, + "position_address": request.position_address, + "percentage": float(request.percentage), + "status": "submitted" + } + + except HTTPException: + raise + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.error(f"Error removing liquidity from CLMM position: {e}", exc_info=True) + raise HTTPException(status_code=500, detail=f"Error removing liquidity from CLMM position: {str(e)}") + @router.post("/clmm/close", response_model=CLMMCollectFeesResponse) async def close_clmm_position( diff --git a/services/gateway_client.py b/services/gateway_client.py index f11feb5..8579f24 100644 --- a/services/gateway_client.py +++ b/services/gateway_client.py @@ -398,19 +398,18 @@ async def clmm_add_liquidity( ) -> Dict: """Add more liquidity to an existing CLMM position""" payload = { - "connector": connector, "network": network, - "address": wallet_address, + "walletAddress": wallet_address, "positionAddress": position_address } if base_token_amount is not None: - payload["baseTokenAmount"] = str(base_token_amount) + payload["baseTokenAmount"] = base_token_amount if quote_token_amount is not None: - payload["quoteTokenAmount"] = str(quote_token_amount) + payload["quoteTokenAmount"] = quote_token_amount if slippage_pct is not None: payload["slippagePct"] = slippage_pct - return await self._request("POST", "clmm/liquidity/add", json=payload) + return await self._request("POST", f"connectors/{connector}/clmm/add-liquidity", json=payload) async def clmm_close_position( self, @@ -435,13 +434,13 @@ async def clmm_remove_liquidity( percentage: float ) -> Dict: """Remove liquidity from a CLMM position (partial)""" - return await self._request("POST", "clmm/liquidity/remove", json={ - "connector": connector, + payload = { "network": network, - "address": wallet_address, + "walletAddress": wallet_address, "positionAddress": position_address, "percentage": percentage - }) + } + return await self._request("POST", f"connectors/{connector}/clmm/remove-liquidity", json=payload) async def clmm_position_info( self,