refactor wip
This commit is contained in:
@ -4,6 +4,28 @@ import os
|
||||
import aiohttp
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from solders import message
|
||||
from jupiter_python_sdk.jupiter import Jupiter, Jupiter_DCA
|
||||
from dexscreener import DexscreenerClient
|
||||
from solana.rpc.types import TokenAccountOpts, TxOpts
|
||||
from solana.rpc.async_api import AsyncClient
|
||||
from solana.transaction import Signature
|
||||
from solana.rpc.websocket_api import connect
|
||||
from solana.rpc.commitment import Confirmed, Processed
|
||||
from solana.transaction import Transaction
|
||||
from spl.token.client import Token
|
||||
from base64 import b64decode
|
||||
import base58
|
||||
from solders.rpc.requests import GetTransaction
|
||||
from solders.signature import Signature
|
||||
from solders.pubkey import Pubkey
|
||||
from solders.keypair import Keypair
|
||||
from solders.transaction import VersionedTransaction
|
||||
from solders.transaction import Transaction
|
||||
from solders.message import Message
|
||||
from solders.instruction import Instruction
|
||||
from solders.hash import Hash
|
||||
from solders.instruction import CompiledInstruction
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
@ -14,20 +36,37 @@ import requests
|
||||
from datetime import datetime
|
||||
from solana.rpc.types import TokenAccountOpts, TxOpts
|
||||
|
||||
|
||||
# # # solders/solana libs (solana_client) # # #
|
||||
from spl.token._layouts import MINT_LAYOUT
|
||||
from solana.rpc.api import Client, Pubkey
|
||||
from spl.token.async_client import AsyncToken
|
||||
|
||||
from spl.token.constants import TOKEN_PROGRAM_ID
|
||||
from borsh_construct import String, CStruct
|
||||
|
||||
# ------------------
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SOLANA_ENDPOINTS = [
|
||||
"wss://api.mainnet-beta.solana.com",
|
||||
]
|
||||
|
||||
PING_INTERVAL = 30
|
||||
SUBSCRIBE_INTERVAL = 1*60 # Resubscribe every 1 minute
|
||||
SUBSCRIBE_INTERVAL = 10*60 # Resubscribe every 1 minute
|
||||
|
||||
from config import (
|
||||
FOLLOWED_WALLET, SOLANA_HTTP_URL, DISPLAY_CURRENCY
|
||||
FOLLOWED_WALLET, SOLANA_HTTP_URL, DISPLAY_CURRENCY, SOLANA_ENDPOINTS
|
||||
)
|
||||
|
||||
from modules.utils import telegram_utils
|
||||
|
||||
# Use the production Solana RPC endpoint
|
||||
solana_client = AsyncClient(SOLANA_HTTP_URL)
|
||||
dexscreener_client = DexscreenerClient()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class SolanaWS:
|
||||
def __init__(self, on_message: Optional[callable] = None):
|
||||
self.websocket = None
|
||||
@ -149,7 +188,7 @@ class SolanaWS:
|
||||
return None
|
||||
|
||||
class SolanaAPI:
|
||||
def __init__(self, process_transaction_callback, on_initial_subscription_callback = None, on_bot_message=None):
|
||||
def __init__(self, process_transaction_callback = None, on_initial_subscription_callback = None, on_bot_message=None):
|
||||
self.process_transaction = process_transaction_callback
|
||||
self.on_initial_subscription = on_initial_subscription_callback
|
||||
self.on_bot_message = on_bot_message,
|
||||
@ -162,6 +201,7 @@ class SolanaAPI:
|
||||
message = await solana_ws.message_queue.get()
|
||||
await self.process_transaction(message)
|
||||
|
||||
_first_subscription = True
|
||||
async def wallet_watch_loop(self):
|
||||
|
||||
solana_ws = SolanaWS(on_message=self.process_transaction)
|
||||
@ -399,8 +439,107 @@ class SolanaAPI:
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print("Error fetching transaction details:", e)
|
||||
|
||||
|
||||
|
||||
async def get_transaction_details_with_retry(transaction_id, retry_delay = 5, max_retries = 16):
|
||||
# wait for the transaction to be confirmed
|
||||
# await async_client.wait_for_confirmation(Signature.from_string(transaction_id))
|
||||
# query every 5 seconds for the transaction details until not None or 30 seconds
|
||||
for _ in range(max_retries):
|
||||
try:
|
||||
tx_details = await self.get_transaction_details_rpc(transaction_id)
|
||||
if tx_details is not None:
|
||||
break
|
||||
except Exception as e:
|
||||
logging.error(f"Error fetching transaction details: {e}")
|
||||
retry_delay = retry_delay * 1.2
|
||||
logging.info(f"({_} of {max_retries}) Waiting for transaction details for {transaction_id}. retry in {retry_delay} s.")
|
||||
await asyncio.sleep(retry_delay)
|
||||
retry_delay *= 1.2
|
||||
return tx_details
|
||||
|
||||
|
||||
async def get_swap_transaction_details(tx_signature_str):
|
||||
t = await self.get_transaction(Signature.from_string(tx_signature_str), max_supported_transaction_version=0)
|
||||
try:
|
||||
parsed_result = {
|
||||
"order_id": None,
|
||||
"token_in": None,
|
||||
"token_out": None,
|
||||
"amount_in": 0,
|
||||
"amount_out": 0,
|
||||
"amount_in_USD": 0,
|
||||
"amount_out_USD": 0,
|
||||
"percentage_swapped": 0
|
||||
}
|
||||
|
||||
instructions = t.value.transaction.transaction.message.instructions
|
||||
# Parse the swap instruction to extract token addresses, amounts, and types
|
||||
for instruction in instructions:
|
||||
if isinstance(instruction, CompiledInstruction):
|
||||
if instruction.program_id == Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"):
|
||||
parsed_info = instruction.parsed.info
|
||||
mint = parsed_info["mint"]
|
||||
amount = float(parsed_info["tokenAmount"]["amount"]) / (10 ** parsed_info["tokenAmount"]["decimals"])
|
||||
|
||||
# Determine token in and token out based on balances
|
||||
if parsed_result["token_in"] is None and amount > 0:
|
||||
parsed_result["token_in"] = mint
|
||||
parsed_result["amount_in"] = amount
|
||||
elif parsed_result["token_out"] is None:
|
||||
parsed_result["token_out"] = mint
|
||||
parsed_result["amount_out"] = amount
|
||||
|
||||
# Calculate percentage swapped
|
||||
if parsed_result["amount_in"] > 0 and parsed_result["amount_out"] > 0:
|
||||
parsed_result["percentage_swapped"] = (parsed_result["amount_out"] / parsed_result["amount_in"]) * 100
|
||||
|
||||
return parsed_result
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Error fetching transaction details: {e}")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def get_token_balance_rpc(wallet_address, token_address):
|
||||
try:
|
||||
accounts = await self.solana_ws.solana_jsonrpc("getTokenAccountsByOwner", [
|
||||
wallet_address,
|
||||
{
|
||||
"mint": token_address
|
||||
}])
|
||||
|
||||
|
||||
if accounts['value']:
|
||||
first_account = accounts['value'][0]['pubkey']
|
||||
balance_data = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "getTokenAccountBalance",
|
||||
"params": [
|
||||
first_account
|
||||
]
|
||||
}
|
||||
balance = self.solana_ws.solana_jsonrpc("getTokenAccountBalance", first_account)
|
||||
|
||||
|
||||
|
||||
if 'value' in balance:
|
||||
amount = float(balance['value']['uiAmount'])
|
||||
logging.debug(f"Balance for {token_address} in {wallet_address}: {amount}")
|
||||
return amount
|
||||
else:
|
||||
logging.debug(f"No balance found for {token_address} in {wallet_address}")
|
||||
return 0
|
||||
else:
|
||||
logging.debug(f"No account found for {token_address} in {wallet_address}")
|
||||
return 0
|
||||
except requests.exceptions.RequestException as e:
|
||||
logging.error(f"Error getting balance for {token_address} in {wallet_address}: {str(e)} \r\n {e}")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -585,6 +724,7 @@ class SolanaDEX:
|
||||
sol_address = "So11111111111111111111111111111111111111112" # Solana's wrapped SOL address
|
||||
return await get_token_prices([sol_address]).get(sol_address, 0.0)
|
||||
|
||||
|
||||
async def get_wallet_balances(wallet_address, doGetTokenName=True):
|
||||
balances = {}
|
||||
logging.info(f"Getting balances for wallet: {wallet_address}")
|
||||
@ -716,4 +856,10 @@ class SolanaDEX:
|
||||
|
||||
# save token info to file
|
||||
await save_token_info()
|
||||
|
||||
async def save_token_info():
|
||||
with open('./logs/token_info.json', 'w') as f:
|
||||
json.dump(TOKENS_INFO, f, indent=2)
|
||||
|
||||
|
||||
SAPI = SolanaAPI()
|
Reference in New Issue
Block a user