swap stable again

This commit is contained in:
Dobromir Popov 2024-11-19 14:42:40 +02:00
parent 1b0c72dc08
commit 69241b1498
3 changed files with 82 additions and 40 deletions

View File

@ -24,7 +24,7 @@ TELEGRAM_BOT_TOKEN="6749075936:AAHUHiPTDEIu6JH7S2fQdibwsu6JVG3FNG0"
DISPLAY_CURRENCY=USD DISPLAY_CURRENCY=USD
#FOLLOW_AMOUNT=3 #FOLLOW_AMOUNT=3
# proportional, xx%, # proportional, xx%,
FOLLOW_AMOUNT=5% FOLLOW_AMOUNT=2%
LIQUIDITY_TOKENS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v,So11111111111111111111111111111111111111112 LIQUIDITY_TOKENS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v,So11111111111111111111111111111111111111112

View File

@ -14,17 +14,18 @@ from jupiter_python_sdk.jupiter import Jupiter, Jupiter_DCA
from dexscreener import DexscreenerClient from dexscreener import DexscreenerClient
from solana.rpc.types import TokenAccountOpts, TxOpts from solana.rpc.types import TokenAccountOpts, TxOpts
from solana.rpc.async_api import AsyncClient from solana.rpc.async_api import AsyncClient
from solana.transaction import Signature
from solana.rpc.websocket_api import connect from solana.rpc.websocket_api import connect
from solana.rpc.commitment import Confirmed, Processed from solana.rpc.commitment import Confirmed, Processed
from solana.transaction import Transaction from solana.rpc.async_api import AsyncClient
from solana.rpc.types import TxOpts
from solana.rpc.commitment import Confirmed, Finalized, Processed
from solana.rpc.core import RPCException
from solana.transaction import Signature, Transaction
from spl.token.client import Token from spl.token.client import Token
from base64 import b64decode from base64 import b64decode
import base58 import base58
from threading import Thread from threading import Thread
from solana.rpc.async_api import AsyncClient
from solana.rpc.types import TxOpts
from solana.rpc.commitment import Confirmed, Finalized, Processed
from solders.rpc.requests import GetTransaction from solders.rpc.requests import GetTransaction
@ -304,7 +305,9 @@ class SolanaAPI:
finally: finally:
receive_task.cancel() receive_task.cancel()
process_task.cancel() process_task.cancel()
except asyncio.CancelledError:
logging.info("Websocket connection cancelled, attempting to reconnect...")
await asyncio.sleep(5) # Wait before reconnecting
except Exception as e: except Exception as e:
logger.error(f"An unexpected error occurred: {e}") logger.error(f"An unexpected error occurred: {e}")
logger.error("".join(traceback.format_exception(None, e, e.__traceback__))) logger.error("".join(traceback.format_exception(None, e, e.__traceback__)))
@ -361,7 +364,7 @@ class SolanaAPI:
await asyncio.sleep(1) await asyncio.sleep(1)
async def get_token_metadata_symbol(self, mint_address): async def get_token_metadata_symbol(self, mint_address):
if mint_address in DEX.TOKENS_INFO and 'symbol' in DEX.TOKENS_INFO[mint_address]: if mint_address in DEX.TOKENS_INFO and DEX.TOKENS_INFO[mint_address] is not None:
return DEX.TOKENS_INFO[mint_address].get('symbol') return DEX.TOKENS_INFO[mint_address].get('symbol')
try: try:
@ -370,6 +373,8 @@ class SolanaAPI:
account_data_data = account_data_result['value']['data'] account_data_data = account_data_result['value']['data']
if 'parsed' in account_data_data and 'info' in account_data_data['parsed']: if 'parsed' in account_data_data and 'info' in account_data_data['parsed']:
account_data_info = account_data_data['parsed']['info'] account_data_info = account_data_data['parsed']['info']
if DEX.TOKENS_INFO[mint_address] is None:
DEX.TOKENS_INFO[mint_address] = {}
if 'decimals' in account_data_info: if 'decimals' in account_data_info:
if mint_address in DEX.TOKENS_INFO: if mint_address in DEX.TOKENS_INFO:
DEX.TOKENS_INFO[mint_address]['decimals'] = account_data_info['decimals'] DEX.TOKENS_INFO[mint_address]['decimals'] = account_data_info['decimals']
@ -780,10 +785,10 @@ class SolanaAPI:
# ) # )
# response_data = response.json() # response_data = response.json()
result = response_data['swapTransaction']
try: try:
response_data['swapTransaction'] result = response_data['swapTransaction']
return response_data['swapTransaction'] return (response_data['swapTransaction'], response_data['lastValidBlockHeight'])
except: except:
raise Exception(response_data['error']) raise Exception(response_data['error'])
# Return the serialized transaction # Return the serialized transaction
@ -793,15 +798,16 @@ class SolanaAPI:
async def follow_move(self,move): async def follow_move(self,move):
try: try:
try: try:
your_balance = 0
your_balances = await DEX.get_wallet_balances(YOUR_WALLET, doGetTokenName=False) your_balances = await DEX.get_wallet_balances(YOUR_WALLET, doGetTokenName=False)
your_balance_info = next((balance for balance in your_balances.values() if balance['address'] == move['token_in']), None) your_balance_info = next((balance for balance in your_balances.values() if balance['address'] == move['token_in']), None)
if your_balance_info is not None: if your_balance_info is not None and 'amount' in your_balance_info and float(your_balance_info['amount']) > 0:
# Use the balance # Use the balance
print(f"Your balance: {your_balance_info['amount']} {move['symbol_in']}") print(f"Your balance: {your_balance_info['amount']} {move['symbol_in']}")
# else: else:
# print(f"No ballance found for {move['symbol_in']}. Skipping move.") print(f"No ballance found for {move['symbol_in']}. Skipping move.")
# await telegram_utils.send_telegram_message(f"No ballance found for {move['symbol_in']}. Skipping move.") await telegram_utils.send_telegram_message(f"No ballance found for {move['symbol_in']}. Skipping move.")
# return return
your_balance = your_balance_info['amount'] your_balance = your_balance_info['amount']
@ -812,7 +818,7 @@ class SolanaAPI:
return return
except Exception as e: except Exception as e:
logging.error(f"Error fetching your balance: {e}") logging.error(f"Error fetching your balance: {str(e)}", exc_info=True)
if FOLLOW_AMOUNT == 'proportional': if FOLLOW_AMOUNT == 'proportional':
return return
@ -898,7 +904,7 @@ class SolanaAPI:
# https://station.jup.ag/api-v6/post-swap # https://station.jup.ag/api-v6/post-swap
#transaction_data = await jupiter.swap( #transaction_data = await jupiter.swap(
transaction_data = await self.swap_on_jupiter( (transaction_data, block) = await self.swap_on_jupiter(
input_mint=move['token_in'], input_mint=move['token_in'],
output_mint=move['token_out'], output_mint=move['token_out'],
amount=amount_lamports, amount=amount_lamports,
@ -933,6 +939,32 @@ class SolanaAPI:
else: else:
logging.warning(f"Failed to get transaction details for {transaction_id}.\n Probably transaction failed. Retrying again...") logging.warning(f"Failed to get transaction details for {transaction_id}.\n Probably transaction failed. Retrying again...")
await asyncio.sleep(3) await asyncio.sleep(3)
except RPCException as rpce:
# Convert to string and parse for key information
error_str = str(rpce)
# Get the main error message
if "Error processing Instruction" in error_str:
# Check for common errors
if "insufficient funds" in error_str:
logging.error("Transaction failed: Insufficient funds")
elif "custom program error: 0x28" in error_str:
logging.error("Transaction failed: Custom program error 40 (0x28) - Likely insufficient funds")
# Parse logs if available
try:
logs = rpce.data.result.logs
if logs:
# Get the last error message from logs
error_logs = [log for log in logs if "Error:" in log]
if error_logs:
logging.error(f"Program error: {error_logs[-1]}")
except AttributeError:
pass
# Full debug logging
logging.debug(f"Full RPC error: {error_str}")
except Exception as e: except Exception as e:
# decode transacion data (try base58/64) # decode transacion data (try base58/64)
# decoded_data = base58.b58decode(transaction_data).decode('utf-8') # decoded_data = base58.b58decode(transaction_data).decode('utf-8')
@ -1071,6 +1103,7 @@ class SolanaDEX:
token_info['symbol'] = await SAPI.get_token_metadata_symbol(token) token_info['symbol'] = await SAPI.get_token_metadata_symbol(token)
token_info['price'] = price token_info['price'] = price
token_info['lastUpdated'] = datetime.now().isoformat() token_info['lastUpdated'] = datetime.now().isoformat()
token_info['address'] = token
return prices return prices
@ -1225,6 +1258,9 @@ class SolanaDEX:
) )
if response.value: if response.value:
if self.TOKENS_INFO is None:
self.TOKENS_INFO = {}
for account in response.value: for account in response.value:
try: try:
parsed_data = account.account.data.parsed parsed_data = account.account.data.parsed
@ -1237,7 +1273,7 @@ class SolanaDEX:
# amount = float(info['tokenAmount']['amount']) # amount = float(info['tokenAmount']['amount'])
# amount = float(amount / 10**decimals) # amount = float(amount / 10**decimals)
token_name = None token_name = None
if mint in self.TOKENS_INFO: if mint in self.TOKENS_INFO and isinstance(self.TOKENS_INFO[mint], dict):
token_name = self.TOKENS_INFO[mint].get('symbol') token_name = self.TOKENS_INFO[mint].get('symbol')
elif doGetTokenName: elif doGetTokenName:
token_name = await SAPI.get_token_metadata_symbol(mint) token_name = await SAPI.get_token_metadata_symbol(mint)
@ -1252,27 +1288,31 @@ class SolanaDEX:
self.TOKENS_INFO[mint] = self.TOKENS_INFO[mint].update(balances[mint]) self.TOKENS_INFO[mint] = self.TOKENS_INFO[mint].update(balances[mint])
try: try:
logging.debug(f"Account balance for {token_name or "N/A"} ({mint}): {amount}") logging.debug(f"Account balance for {token_name or 'N/A'} ({mint}): {amount}")
except Exception as e: except Exception as e:
logging.error(f"Error logging account balance: {str(e)}") logging.error(f"Error logging account balance: {str(e)}", exc_info=True)
else: else:
logging.warning(f"Unexpected data format for account: {account}") logging.warning(f"Unexpected data format for account: {account}")
except Exception as e: except Exception as e:
logging.error(f"Error parsing account data: {str(e)}") logging.error(f"Error parsing account data: {str(e)}", exc_info=True)
self.TOKENS_INFO["lastUpdated"] = datetime.now().isoformat() self.TOKENS_INFO["lastUpdated"] = datetime.now().isoformat()
# sol_balance = await self.solana_client.get_balance(Pubkey.from_string(wallet_address)) sol_balance = await self.solana_client.get_balance(Pubkey.from_string(wallet_address))
# if sol_balance.value is not None: if sol_balance.value is not None:
# balances['SOL'] = { mint = 'So11111111111111111111111111111111111111112'
# 'name': 'SOL', balances[mint] = {
# 'address': 'SOL', 'name': 'SOL',
# 'amount': sol_balance.value / 1e9 'address': mint,
# } 'amount': sol_balance.value / 1e9,
# else: 'decimals': 9
# logging.warning(f"SOL balance response missing for wallet: {wallet_address}") }
self.TOKENS_INFO[mint] = {'symbol': token_name}
self.TOKENS_INFO[mint] = self.TOKENS_INFO[mint].update(balances[mint])
else:
logging.warning(f"SOL balance response missing for wallet: {wallet_address}")
except Exception as e: except Exception as e:
logging.error(f"Error getting wallet balances: {str(e)} {e.error_msg}") logging.error(f"Error getting wallet balances: {str(e)}", exc_info=True)
if response and response.value: if response and response.value:
logging.info(f"Found {len(response.value)} ({len(balances)} non zero) token accounts for wallet: {wallet_address}") logging.info(f"Found {len(response.value)} ({len(balances)} non zero) token accounts for wallet: {wallet_address}")
else: else:

View File

@ -335,13 +335,15 @@ def init_app(tr_handler=None):
tr["token_out"] tr["token_out"]
) )
# ToDo - optimize # ToDo - optimize
# prices = await SolanaAPI.DEX.get_token_prices( prices = await SolanaAPI.DEX.get_token_prices(
# [tr["token_in"], tr["token_out"]] [tr["token_in"], tr["token_out"]]
# ) )
# tr["token_in_price"] = prices.get(tr["token_in"], 0) await SolanaAPI.DEX.save_token_info()
# tr["token_out_price"] = prices.get(tr["token_out"], 0)
# tr["value_in_USD"] = prices.get(tr["token_in"], 0) * tr["amount_in"] tr["token_in_price"] = prices.get(tr["token_in"], 0)
# tr["value_out_USD"] = prices.get(tr["token_out"], 0) * tr["amount_out"] tr["token_out_price"] = prices.get(tr["token_out"], 0)
tr["value_in_USD"] = prices.get(tr["token_in"], 0) * tr["amount_in"]
tr["value_out_USD"] = prices.get(tr["token_out"], 0) * tr["amount_out"]
# notification = f"<b>Got WH notification:</b>: {tr['amount_in']} {tr['symbol_in'] or tr["token_in"]} swapped for {tr['amount_out']} {tr['symbol_out']} ${tr['value_out_USD']}\n" # notification = f"<b>Got WH notification:</b>: {tr['amount_in']} {tr['symbol_in'] or tr["token_in"]} swapped for {tr['amount_out']} {tr['symbol_out']} ${tr['value_out_USD']}\n"
# logging.info(notification) # logging.info(notification)