Solana RPC Timeout Fix

Tạo vào: 30 tháng 11, 2024

Trả lời bằng O1 Preview bởi Chat01

Câu hỏi

Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/solana/exceptions.py", line 60, in argument_decorator
return await func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/solana/rpc/providers/async_http.py", line 59, in make_request
raw = await self.make_request_unparsed(body)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/solana/rpc/providers/async_http.py", line 65, in make_request_unparsed
raw_response = await self.session.post(**request_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1861, in post
return await self.request(
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1542, in request
return await self.send(request, auth=auth, follow_redirects=follow_redirects)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1631, in send
response = await self._send_handling_auth(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1659, in _send_handling_auth
response = await self._send_handling_redirects(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1696, in _send_handling_redirects
response = await self._send_single_request(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpx/_client.py", line 1732, in _send_single_request
response = await transport.handle_async_request(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 393, in handle_async_request
with map_httpcore_exceptions():
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/contextlib.py", line 158, in exit
self.gen.throw(value)
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 118, in map_httpcore_exceptions
raise mapped_exc(message) from exc
httpx.ReadTimeout

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/app/main.py", line 25, in send_notifications
new_links = set((await get_latest_sol_transactions(wallet.wallet_hash)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/parsers/crypto/solana_check.py", line 56, in get_latest_sol_transactions
res = await client.get_signatures_for_address(Pubkey.from_string(address))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/solana/rpc/async_api.py", line 360, in get_signatures_for_address
return await self._provider.make_request(body, GetSignaturesForAddressResp)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/solana/exceptions.py", line 62, in argument_decorator
raise internal_exception_cls(exc, func, *args, **kwargs) from exc
solana.exceptions.SolanaRpcException

такая ошибка возникает в моем коде при вызове насколько я понимаю вот этого кода:
async def get_latest_sol_transactions(address: str) -> list[str]:
"""
Получить последние n транзакций для указанного адреса в сети Solana.

text
:param address: Строка с адресом кошелька Solana. :return: Список подписей последних транзакций. """ count = int(os.getenv('SOLANA_WALLETS_NUM')) async with AsyncClient('https://api.mainnet-beta.solana.com') as client: # Запрос подписей транзакций для указанного адреса res = await client.get_signatures_for_address(Pubkey.from_string(address)) if res.value is not None: # Возвращаем только первые `count` транзакций return [str(tx.signature) for tx in res.value[:count]] else: logging.info("No transactions found for the given address.") return []

через функцию:
async def send_notifications(bot: Bot, timeout: int):
"""
Функция для отправки уведомлений
:param bot: Инстанс бота который будет отправлять сообщения
:param timeout: Время между проверками новых транзакций (в секундах), настраивается в .env
"""
while True:
wallets = await PostgresRequests().get_all_wallets()
for wallet in wallets:
old_links = await get_links(wallet_hash=wallet.wallet_hash)
new_links = set((await get_latest_sol_transactions(wallet.wallet_hash)))
diff = new_links - old_links
for link in diff:
transaction = await get_sol_transaction(link)
text = f"Транзакция: {transaction["transaction"]} \n"
text += f"Отправитель: {transaction["sender"]} \n"
text += f"Получатель: {transaction["receiver"]} \n"
text += f"Сумма: {transaction["amount"]} SOL \n"
await bot.send_message(chat_id=wallet.tg_id, text=text)

text
await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

как мне это пофиксить?

Suy nghĩ

алюзорский это означает, что происходит ошибка traceback в коде Python, связанная с асинхронными вызовами API Solana.

Анализируя код

Окей, давайте разберемся: код пытается получить последние транзакции для указанного адреса в сети Solana, но возникает ошибка, связанная с тайм-аутом HTTP-запроса.

Ищу способы

Определяю проблему с ошибкой httpx.ReadTimeout в коде, связанном с HTTP-запросами к Solana AsyncClient.

Анализ возможных причин

Сейчас я разбираюсь с возможными причинами превышения времени ожидания: проблемы с сетевым подключением, перегруженность конца Solana RPC и слишком короткий тайм-аут.

Устанавливаю временные ограничения

Указываю тайм-аут при инициализации AsyncClient, чтобы избежать проблемы с временем ожидания чтения.

Trả lời

The error you're encountering is due to a httpx.ReadTimeout exception, which is raised when the HTTP request to the Solana RPC endpoint takes longer than the allowed timeout period. This exception is then re-raised as a solana.exceptions.SolanaRpcException by the Solana client library.

Why This Happens:

  • Network Latency: The Solana RPC endpoint (https://api.mainnet-beta.solana.com) might be experiencing high load or network latency, causing requests to take longer than expected.
  • Default Timeout: The default timeout for the HTTP requests might be too short for your use case.
  • Transient Issues: Temporary network issues can also cause timeouts.

How to Fix It:

  1. Increase the Timeout: You can specify a longer timeout when initializing the AsyncClient from the solana-py library. This will give the server more time to respond before a timeout exception is raised.

  2. Implement Retry Logic: Wrap your request in a try-except block and implement retries with exponential backoff in case of transient failures.

Here's how you can modify your code:

python
import asyncio import logging import os from solana.rpc.async_api import AsyncClient from solana.publickey import PublicKey async def get_latest_sol_transactions(address: str) -> list[str]: """ Получить последние n транзакций для указанного адреса в сети Solana. :param address: Строка с адресом кошелька Solana. :return: Список подписей последних транзакций. """ count = int(os.getenv('SOLANA_WALLETS_NUM', '10')) # Default to 10 if not set timeout = 30 # Set the timeout to 30 seconds max_retries = 3 # Maximum number of retries retry_delay = 5 # Initial delay between retries for attempt in range(max_retries): try: async with AsyncClient('https://api.mainnet-beta.solana.com', timeout=timeout) as client: # Запрос подписей транзакций для указанного адреса res = await client.get_signatures_for_address(PublicKey(address)) if res.value is not None: # Возвращаем только первые `count` транзакций return [str(tx.signature) for tx in res.value[:count]] else: logging.info("No transactions found for the given address.") return [] except Exception as e: logging.error(f"Attempt {attempt + 1}: Failed to get transactions due to {e}") if attempt < max_retries - 1: await asyncio.sleep(retry_delay) retry_delay *= 2 # Exponential backoff else: raise e # Re-raise the exception if max retries exceeded

Explanation:

  • Timeout Parameter: We set the timeout parameter when initializing AsyncClient. This adjusts the maximum time to wait for a response.

    python
    async with AsyncClient('https://api.mainnet-beta.solana.com', timeout=timeout) as client:
  • Retry Logic: We implement a retry mechanism with exponential backoff. If a transient error occurs, the code waits and retries the request.

  • Logging: We added logging to keep track of failed attempts, which can help with debugging.

Additional Recommendations:

  • Environment Variables: Ensure that the SOLANA_WALLETS_NUM environment variable is set appropriately or provide a default value.

  • Error Handling: Be cautious with broad exception handling (except Exception as e). If you want to handle specific exceptions (like timeouts), you can catch httpx.ReadTimeout and solana.exceptions.SolanaRpcException separately.

  • Thread Safety: If this code runs in a multi-threaded environment, ensure that the AsyncClient is used correctly within each coroutine.

  • Update Dependencies: Make sure all your libraries (solana-py, httpx, etc.) are up to date, as newer versions might have fixes for related issues.

Example with Specific Exception Handling:

python
from httpx import ReadTimeout from solana.exceptions import SolanaRpcException ... except (ReadTimeout, SolanaRpcException) as e: logging.error(f"Attempt {attempt + 1}: Timeout occurred: {e}") ...

Implementing in Your send_notifications Function:

Ensure that any exceptions not handled within get_latest_sol_transactions are appropriately managed in your send_notifications function to prevent the entire notification loop from crashing due to an unhandled exception.

Final Notes:

  • By increasing the timeout and adding retries, your application becomes more robust against transient network issues and server load spikes.
  • Always test these changes in a controlled environment before deploying them to production.

Câu hỏi

INFO:aiogram.dispatcher:Run polling for bot @xdxdssw1_bot id=7678605295 - 'worktest'
INFO:httpx:HTTP Request: POST https://api.mainnet-beta.solana.com "HTTP/1.1 200 OK"
INFO:httpx:HTTP Request: POST https://api.mainnet-beta.solana.com "HTTP/1.1 200 OK"
INFO:httpx:HTTP Request: POST https://api.mainnet-beta.solana.com "HTTP/1.1 429 Too Many Requests"
ERROR:root:Timeout occurred:
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-3' coro=<send_notifications() done, defined at /app/main.py:15> exception=TypeError("'NoneType' object is not iterable")>
Traceback (most recent call last):
File "/app/main.py", line 25, in send_notifications
new_links = set((await get_latest_sol_transactions(wallet.wallet_hash)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not iterable

Suy nghĩ

Понимание проблемы: Пользователь сталкивается с новой ошибкой TypeError, которая возникает при попытке создать множество из NoneType. Это наводит на мысль о необходимости обработки нежелательных значений перед их использованием.

Ищу причины

Проблема в функции send_notifications, где await get_latest_sol_transactions(wallet.wallet_hash) возвращает None вместо списка. Нужно выяснить, что могло вызвать такую ситуацию.

Проверяю транзакции

На данный момент я смотрю на функцию get_latest_sol_transactions, которая запрашивает последние транзакции для указанного адреса в сети Solana, используя клиента AsyncClient и асинхронный клиент.

Пересматриваю детали

Оказывается, код в ответе помнит правильные шаги, но сейчас я замечаю, что Solana RPC возвращает 429 Too Many Requests.

Рассматриваю обработку исключений

Я думаю, что код логирует исключения и повторяет попытку, если не превышено максимальное количество попыток. Однако, если исключение обрабатывается и функция достигает конца без возврата значения, это приводит к None.

Понимание исключений

Пытаюсь понять, как собака перехватывает исключения. Возможно, она где-то прячется или не до конца реализована.

Возможное решение функции

async def get_latest_sol_transactions(address: str) -> list[str]: count = int(os.getenv('SOLANA_WALLETS_NUM', '10')) timeout = 30 max_retries = 3 retry_delay = 5

text
for attempt in range(max_retries): try: async with AsyncClient('https://api.mainnet-beta.solana.com', timeout=timeout) as client: res = await client.get_signatures_for_address(PublicKey(address)) if res.value is not None: return [str(tx.signature) for tx in res.value[:count]] else: logging.info("No transactions found for the given address.") return [] except Exception as e: logging.error(f"Attempt {attempt + 1}: Failed to get transactions due to {e}") if attempt < max_retries - 1: await asyncio.sleep(retry_delay) retry_delay *= 2 else: raise e return []

Рассматриваю роли

Похоже, функция должна всегда возвращать список, даже при исключениях. Это предотвратит возвращение None и упрощает обработку ошибок.

Убеждаюсь в точности

Проверяю, что get_latest_sol_transactions возвращает список в любом случае, чтобы избежать ошибки.

Интеграция начислений оплаты с Solana

Предлагаю пути

Здесь пробую разные варианты обработки исключений: повторные отбрасывания, возврат пустого списка или перехват исключений в вызывающем коде.

Использование асинхронных функций для отправки уведомлений о транзакциях, обработка ошибок и обновление данных о кошельках.

Обрабатываю исключения

Окей, кажется, что обработка исключений в функции get_latest_sol_transactions не учитывает определенные крайние случаи, как, например, ошибку 429 Too Many Requests.

Похоже, что сообщение неописуемой ошибки может быть связано с тем, что исключение e не содержит полезного сообщения. На сервере Solana используется HTTP-статус 429, что указывает на превышение лимита запросов.

Пробую разные подходы

Анализирую возможные методы: поймать исключение, повторно выбросить его, захватить и логировать детали, или вернуть пустой список в случае неудачи. Интересно, как это повлияет на результат.

Решаем проблему с Solana RPC

Я разбираюсь с ошибкой 429 Too Many Requests в Solana RPC, предлагая методы, такие как лимитирование частоты запросов, использование другого endpoin'a Solana RPC или добавление задержки между запросами.

Управляю частотой

Для обработки ограничений частоты запросов Solana RPC в 429 Too Many Requests, следует добавлять задержки или уменьшать частоту запросов.

Настраиваю параметры

Устанавливаю тайм-аут и максимальное количество попыток для запроса транзакций по адресу кошелька Solana.

Обновляю код

Теперь get_latest_sol_transactions пробрасывает исключение, если превышено максимальное количество попыток, а send_notifications это исключение ловит.

Управляю временем и ресурсами

Пробую перезапросы, увеличивая задержку. Последняя ошибка срабатывает только при превышении лимита попыток.

Обновляю списки

Задерживаю обработку списка кошельков для частоты запросов, игнорируя ошибку из-за превышения 429.

Реализуя обработку ошибок

Я адаптирую get_latest_sol_transactions, чтобы отслеживать HTTP статус 429, корректируя задержки повторных попыток и уровни неудач.

Изучаю пути исключения

Предлагаю изменить обработку исключений, чтобы захватывать все исключения и логировать их с repr(e), чтобы получить более подробную информацию об ошибках.

Обрабатываю исключения

Проверяю, что get_latest_sol_transactions не возвращает None, исключения обрабатываются send_notifications, и я изучаю проблемы с Solana RPC endpoint.

Trả lời

The new error you're encountering indicates that your get_latest_sol_transactions function is returning None, and when you attempt to create a set from it in send_notifications, Python raises a TypeError because None is not iterable.

Understanding the Error:

  • HTTP 429 Too Many Requests: The log shows that you're receiving a 429 Too Many Requests response from the Solana RPC endpoint (https://api.mainnet-beta.solana.com). This means you've exceeded the rate limit for requests to this endpoint.

  • Function Returns None: Due to the exception, your function get_latest_sol_transactions does not return any value, which defaults to None in Python.

  • TypeError in send_notifications: When send_notifications tries to execute set((await get_latest_sol_transactions(wallet.wallet_hash))), it fails because you cannot create a set from None.

How to Fix It:

  1. Ensure the Function Always Returns a List or Raises an Exception:

    Modify your get_latest_sol_transactions function to ensure that it either returns a list or raises an exception in all code paths. This prevents it from returning None.

  2. Handle Exceptions in send_notifications:

    In your send_notifications function, wrap the call to get_latest_sol_transactions in a try-except block to handle any exceptions that might occur.

  3. Implement Rate Limiting or Use an Alternative RPC Endpoint:

    Since you're hitting rate limits, consider using a different Solana RPC endpoint or implement rate limiting in your code to avoid sending too many requests in a short period.

Modified Code Examples:

1. Modify get_latest_sol_transactions Function:

python
import asyncio import logging import os from solana.rpc.async_api import AsyncClient from solana.publickey import PublicKey from solana.exceptions import SolanaRpcException async def get_latest_sol_transactions(address: str) -> list[str]: """ Get the latest n transactions for the specified Solana address. :param address: Solana wallet address as a string. :return: List of signatures of the latest transactions. """ count = int(os.getenv('SOLANA_WALLETS_NUM', '10')) # Default to 10 if not set timeout = 30 # Timeout in seconds max_retries = 3 # Maximum number of retries retry_delay = 5 # Initial delay between retries for attempt in range(1, max_retries + 1): try: async with AsyncClient('https://api.mainnet-beta.solana.com', timeout=timeout) as client: res = await client.get_signatures_for_address(PublicKey(address)) if res.value is not None: return [str(tx.signature) for tx in res.value[:count]] else: logging.info("No transactions found for the given address.") return [] except Exception as e: logging.error(f"Attempt {attempt}: Failed to get transactions due to: {repr(e)}") if attempt < max_retries: await asyncio.sleep(retry_delay) retry_delay *= 2 # Exponential backoff else: logging.error(f"Max retries exceeded for address {address}. Raising exception.") raise # Re-raise the last exception # In case all retries are exhausted without raising an exception return []

2. Modify send_notifications Function:

python
async def send_notifications(bot: Bot, timeout: int): """ Function to send notifications. :param bot: Bot instance that will send messages. :param timeout: Time between checks for new transactions (in seconds), configured in .env. """ while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Failed to get transactions for wallet {wallet.wallet_hash} due to: {repr(e)}") continue # Skip to the next wallet diff = new_links - old_links for link in diff: transaction = await get_sol_transaction(link) text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += f"Сумма: {transaction['amount']} SOL \n" await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

3. Handle Rate Limiting:

Since you're receiving 429 Too Many Requests, you should handle this specifically:

python
from httpx import HTTPStatusError async def get_latest_sol_transactions(address: str) -> list[str]: # ... (previous code) ... except SolanaRpcException as e: if '429' in str(e): logging.error(f"Received 429 Too Many Requests for address {address}. Retrying after delay.") if attempt < max_retries: await asyncio.sleep(retry_delay) retry_delay *= 2 else: logging.error(f"Max retries exceeded due to rate limiting. Raising exception.") raise else: logging.error(f"Solana RPC Exception occurred: {repr(e)}") raise except Exception as e: logging.error(f"Attempt {attempt}: Unexpected error: {repr(e)}") raise

4. Consider Using an Alternative Solana RPC Endpoint:

The public Solana RPC endpoint (https://api.mainnet-beta.solana.com) has strict rate limits. You can use alternative endpoints or services like:

These services often provide higher rate limits or even unlimited requests for free tiers.

5. Implement Client-Side Rate Limiting:

If changing the endpoint is not an option, implement rate limiting in your code to prevent hitting the server's limits:

python
import asyncio from asyncio_throttle import Throttler throttler = Throttler(rate_limit=5, period=1) # Allow 5 requests per second async def get_latest_sol_transactions(address: str) -> list[str]: # ... (previous code) ... async with throttler: async with AsyncClient('https://api.mainnet-beta.solana.com', timeout=timeout) as client: # ... (rest of the code) ...

Note: You'll need to install asyncio-throttle with pip install asyncio-throttle.

6. Adjust Logging for Clarity:

Make sure your logging statements include repr(e) to capture the full exception information, especially since some exceptions may not include a message.

Example:

python
logging.error(f"Attempt {attempt}: Failed to get transactions due to: {repr(e)}")

Summary:

  • Ensure that get_latest_sol_transactions always returns a list or raises an exception.
  • Handle exceptions properly in send_notifications to prevent the entire loop from crashing.
  • Address the rate limiting issue by using an alternative RPC endpoint or implementing client-side rate limiting.
  • Adjust your logging to capture full exception details for better debugging.

Final Thoughts:

By making these adjustments, your application should become more robust, handle exceptions gracefully, and comply with the rate limits imposed by the Solana RPC endpoint.

Câu hỏi

File "/usr/local/lib/python3.12/site-packages/aiogram/dispatcher/router.py", line 141, in _wrapped
return await self._propagate_event(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiogram/dispatcher/router.py", line 174, in _propagate_event
response = await router.propagate_event(update_type=update_type, event=event, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiogram/dispatcher/router.py", line 146, in propagate_event
return await observer.wrap_outer_middleware(_wrapped, event=event, data=kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiogram/dispatcher/router.py", line 141, in _wrapped
return await self._propagate_event(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiogram/dispatcher/router.py", line 166, in _propagate_event
response = await observer.trigger(event, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiogram/dispatcher/event/telegram.py", line 121, in trigger
return await wrapped_inner(event, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiogram/dispatcher/event/handler.py", line 43, in call
return await wrapped()
^^^^^^^^^^^^^^^
File "/app/handlers/wallets.py", line 82, in enable_tracking
links = await get_latest_sol_transactions(address)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/parsers/crypto/solana_check.py", line 63, in get_latest_sol_transactions
res = await client.get_signatures_for_address(Pubkey(address))
^^^^^^^^^^^^^^^
ValueError: expected a sequence of length 32 (got 44)
ERROR:root:Attempt 3: Failed to get transactions due to: ValueError('expected a sequence of length 32 (got 44)')
ERROR:root:Max retries exceeded for address 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq. Raising exception.
ERROR:root:Failed to get transactions for wallet 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq due to: ValueError('expected a sequence of length 32 (got 44)')
INFO:sqlalchemy.engine.Engine:BEGIN (implicit)
INFO:sqlalchemy.engine.Engine:SELECT wallets.id, wallets.tg_id, wallets.wallet_hash, wallets.seed
FROM wallets
INFO:sqlalchemy.engine.Engine:[cached since 75.17s ago] ()
INFO:sqlalchemy.engine.Engine:ROLLBACK
ERROR:root:Attempt 1: Failed to get transactions due to: ValueError('expected a sequence of length 32 (got 44)')

Suy nghĩ

Повторяющиеся детали

В разговоре между пользователем и помощником обсуждаются ошибки, возникающие в коде. Помощник предлагает исправления, а затем появляется новая ошибка с трассировкой.

Анализирую ошибки и детали

Я просматриваю стек вызовов и замечаю ключевую ошибку в первом файле, связанного с пропагандой события и объектом файл, модуль информации о файле, а затем продолжаю заявление об ошибке ValueError, упомянутой в других частях стека.

Исправляю ошибку

Пробую получить транзакции для кошелька, но получаю ValueError: expected a sequence of length 32 (got 44). Это связано с созданием объекта PublicKey.

Проверяю структуру адреса

Разбираю длину адреса, предоставленного пользователем, и замечаю несоответствие с ожидаемой длиной 32 символа. Похоже, проблема в передаче адреса неправильного типа в конструктор PublicKey.

Разглядываю класс

Анализирую класс Solana PublicKey из пакета solana. Заметно, что при создании с адресом использован неправильный вызов.

Использование и формирование

Проверяю, как класс PublicKey из пакета solana генерирует публичный ключ из base58-кодированного адреса.

Перемещаюсь по библиотеке

Интересно, что большая часть проблем в импортировании библиотеки связана с использованием Pubkey вместо PublicKey.

Теперь я объясняю, что ошибка "expected a sequence of length 32 (got 44)" указывает на ожидание последовательности длиной 32 байта, но получена последовательность длиной 44 байта (длина строки, закодированной в base58).

Использование Pubkey вместо PublicKey может привести к ошибке длины байтовой последовательности.

Изменяю код

Обновляю импорт и метод, добавляя объект PublicKey для корректного создания public key из адреса в base58.

Проверяю и обновляю

Проверяю и обновляю, что PublicKey из solana импортирован правильно и адрес соответствует требованиям Solana.

Trả lời

The error you're encountering is:

ValueError: expected a sequence of length 32 (got 44)

This error occurs when constructing a PublicKey object with an input that doesn't meet the expected format or length. Specifically, the PublicKey constructor expects a sequence of bytes of length 32 (the length of a Solana public key in bytes), but it's receiving an input of length 44.

Understanding the Error:

  • Incorrect Class Used: It appears that you're using Pubkey instead of PublicKey.
  • Address Format Mismatch: The address you're providing is a base58-encoded string, which is 44 characters long. When decoded, it should result in 32 bytes.
  • Class Expectations: The Pubkey class may be expecting a raw byte sequence, not a base58-encoded string.

Solution:

  1. Use the Correct Class:

    Ensure you're importing and using the PublicKey class from the solana.publickey module, not Pubkey.

    python
    from solana.publickey import PublicKey
  2. Modify Your Function:

    Update your get_latest_sol_transactions function to use PublicKey:

    python
    async def get_latest_sol_transactions(address: str) -> list[str]: """ Получить последние n транзакций для указанного адреса в сети Solana. :param address: Строка с адресом кошелька Solana. :return: Список подписей последних транзакций. """ count = int(os.getenv('SOLANA_WALLETS_NUM', '10')) # Default to 10 if not set timeout = 30 # Timeout in seconds max_retries = 3 # Maximum number of retries retry_delay = 5 # Initial delay between retries for attempt in range(1, max_retries + 1): try: async with AsyncClient('https://api.mainnet-beta.solana.com', timeout=timeout) as client: res = await client.get_signatures_for_address(PublicKey(address)) if res.value is not None: return [str(tx.signature) for tx in res.value[:count]] else: logging.info("No transactions found for the given address.") return [] except Exception as e: logging.error(f"Attempt {attempt}: Failed to get transactions due to: {repr(e)}") if attempt < max_retries: await asyncio.sleep(retry_delay) retry_delay *= 2 # Exponential backoff else: logging.error(f"Max retries exceeded for address {address}. Raising exception.") raise # Re-raise the last exception # In case all retries are exhausted without raising an exception return []
  3. Explanation:

    • PublicKey vs. Pubkey: The PublicKey class is designed to handle base58-encoded strings directly, whereas Pubkey might not.
    • Base58 Decoding: The PublicKey constructor will internally decode the base58-encoded address to the required 32-byte sequence.
    • Correct Import: Ensure you're importing PublicKey from the correct module.
  4. Check Your Imports:

    Make sure you have the correct import statements:

    python
    from solana.publickey import PublicKey
  5. Verify the Address:

    Ensure that the address you're using is a valid Solana public key in base58 format.

Additional Notes:

  • Library Version: Ensure you're using a compatible version of the solana-py library. Updating to the latest version can resolve unexpected issues.

    bash
    pip install --upgrade solana
  • Error Handling: The exception handling in your function will now catch and log the error, then retry as per your logic.

  • Consistent Class Usage: Using PublicKey consistently throughout your code will prevent similar errors.

Alternative Approach (If Needed):

If you still encounter issues, you can manually decode the base58-encoded address:

python
from solana.publickey import PublicKey from base58 import b58decode async def get_latest_sol_transactions(address: str) -> list[str]: # ... previous code ... try: pubkey_bytes = b58decode(address) res = await client.get_signatures_for_address(PublicKey(pubkey_bytes)) # ... rest of your code ...

However, this shouldn't be necessary if PublicKey(address) works correctly.

Implementing in Your enable_tracking Function:

Ensure that you update the enable_tracking function accordingly:

python
async def enable_tracking(): # ... previous code ... links = await get_latest_sol_transactions(address) # ... rest of your code ...

Summary:

  • Replace Pubkey with PublicKey.
  • Ensure correct import from solana.publickey.
  • Verify that the address is a valid base58-encoded Solana public key.
  • Update your code to handle exceptions and retries appropriately.

By making these changes, the ValueError should be resolved, and your application should function as expected.


Final Thoughts:

  • Testing: After making these changes, test your application with a known valid address to ensure everything works.
  • Logging: Continue to monitor logs for any other exceptions and handle them as needed.
  • Documentation: Refer to the solana-py documentation for any additional guidance.

If you have any more questions or encounter further issues, feel free to ask!

Câu hỏi

ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-3' coro=<send_notifications() done, defined at /app/main.py:15> exception=KeyError('program')>
Traceback (most recent call last):
File "/app/main.py", line 33, in send_notifications
transaction = await get_sol_transaction(link)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/parsers/crypto/solana_check.py", line 127, in get_sol_transaction
if instruction['program'] == 'system':
~~~~~~~~~~~^^^^^^^^^^^
KeyError: 'program'

async def get_sol_transaction(tx_signature: str) -> dict | None:
"""
Получение информации о транзакции Solana по ее подписи.

text
:param tx_signature: Подпись транзакции Solana в формате строки :return: Словарь с информацией о транзакции (подпись, отправитель, получатель, сумма) / None в случае ошибок """ url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [ tx_signature, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 0 } ] } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json() if 'result' not in response or response['result'] is None: logging.error("Транзакция не найдена или произошла ошибка.") return result = response['result'] # Информация о транзакции transaction = result['transaction'] message = transaction['message'] account_keys = message['accountKeys'] instructions = message['instructions'] # Инициализация переменных получателя и суммы receiver = None amount_lamports = None # Отправитель — первый в списке accountKeys (fee payer) sender = account_keys[0]['pubkey'] # Поиск инструкции перевода в списке инструкций for instruction in instructions: if instruction['program'] == 'system': parsed = instruction.get('parsed') if parsed and parsed['type'] == 'transfer': info = parsed['info'] receiver = info['destination'] amount_lamports = int(info['lamports']) break # Предполагаем, что нас интересует первая инструкция перевода if amount_lamports is not None: amount_sol = amount_lamports / 1e9 else: amount_sol = None return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount": amount_sol, }

Suy nghĩ

Читаю сообщение

Пользователь передает сообщение об ошибке и фрагмент кода.

Обычное взаимодействие

Получение информации о транзакциях Solana через API, включая подписи и отправителей.

Размышляю сердио ошибками

Итак, задача - понять причину ошибки и предложить решение. Оказывается, в словаре 'instruction' может не быть ключа 'program'. Изучаю структуру данных 'instructions' в ответе Solana 'getTransaction'.

Анализируя сценарии

Предлагаю модифицировать код для обработки сценариев, когда ключ 'program' отсутствует в инструкциях. Это поможет точно управлять различными типами транзакций.

Ищу ключи

Я просматриваю код, чтобы учесть возможное отсутствие ключа 'program'. Главное, обеспечивать корректную обработку ошибок, используя logging для диагностики.

Уточняю обработку

Я программирую цикл для обработки инструкций, проверяя наличие ключа 'program' и сортируя инструкции по типу.

Trả lời

The KeyError: 'program' you're encountering indicates that in some of the instructions within the instructions list, the 'program' key does not exist. This is causing your code to fail when it tries to access instruction['program'].

Understanding the Error:

  • Instruction Variations: In the Solana transaction data, instructions can vary in structure depending on the type of transaction and how the instruction is encoded.
  • Missing Keys: Some instructions might not have the 'program' key but instead have a 'programId' key, especially if they are not parsed or are in a different format.

Solution:

You need to adjust your code to handle cases where the 'program' key might not be present in an instruction. Here's how you can modify your code:

  1. Use .get() Method:

    Replace instruction['program'] with instruction.get('program'). This will return None if the key doesn't exist, preventing a KeyError.

  2. Add a Check for the Key:

    Before accessing instruction['program'], check if the key exists in the instruction.

  3. Handle Instructions with 'programId':

    If the 'program' key is not present, check for the 'programId' key, which might contain the program ID in base58 format.

  4. Log the Instruction for Debugging:

    Logging the instruction when the 'program' key is missing can help you understand the structure of such instructions.

Modified Code:

Here's the updated get_sol_transaction function with the necessary adjustments:

python
import aiohttp import logging async def get_sol_transaction(tx_signature: str) -> dict | None: """ Получение информации о транзакции Solana по ее подписи. :param tx_signature: Подпись транзакции Solana в формате строки :return: Словарь с информацией о транзакции (подпись, отправитель, получатель, сумма) / None в случае ошибок """ url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [ tx_signature, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 0 } ] } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json() if 'result' not in response or response['result'] is None: logging.error("Транзакция не найдена или произошла ошибка.") return result = response['result'] # Информация о транзакции transaction = result['transaction'] message = transaction['message'] account_keys = message['accountKeys'] instructions = message['instructions'] # Инициализация переменных получателя и суммы receiver = None amount_lamports = None # Отправитель — первый в списке accountKeys (fee payer) sender = account_keys[0]['pubkey'] # Поиск инструкции перевода в списке инструкций for instruction in instructions: program = instruction.get('program') if program == 'system': parsed = instruction.get('parsed') if parsed and parsed.get('type') == 'transfer': info = parsed.get('info') if info: receiver = info.get('destination') amount_lamports = int(info.get('lamports', 0)) break # Предполагаем, что нас интересует первая инструкция перевода else: # Optionally, handle other types of instructions or log them logging.debug(f"Skipping instruction without 'program' key or non-system program: {instruction}") if amount_lamports is not None: amount_sol = amount_lamports / 1e9 else: amount_sol = None return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount": amount_sol, }

Explanation:

  • Using .get() Method:

    • instruction.get('program') safely retrieves the value associated with 'program' or returns None if the key doesn't exist.
    • This prevents the KeyError exception when the key is missing.
  • Additional Checks:

    • Before accessing nested keys, use .get() to ensure they exist.
    • For example, parsed.get('type') and info.get('destination').
  • Logging:

    • Added a logging.debug statement to log instructions that are skipped. This can help you understand what other types of instructions are present in the transaction.
  • Handling Amount:

    • Used info.get('lamports', 0) to provide a default value in case 'lamports' is missing.

Additional Recommendations:

  1. Check for 'programId' Key:

    Sometimes, the instruction might have a 'programId' key instead of 'program'. You can adjust your code to handle this:

    python
    program = instruction.get('program') or instruction.get('programId') if program == 'system' or program == '11111111111111111111111111111111': # Proceed with parsing

    Note: '11111111111111111111111111111111' is the program ID for the system program in Solana.

  2. Handle Inner Instructions:

    Some transactions include inner instructions that might contain the transfer details. You might need to check meta.innerInstructions.

    python
    meta = result.get('meta', {}) inner_instructions = meta.get('innerInstructions', []) # Process inner instructions similarly
  3. Robust Error Handling:

    Add exception handling around sections that might fail to prevent the function from crashing.

    python
    try: # Parsing logic except Exception as e: logging.error(f"Error parsing transaction {tx_signature}: {e}") return

Testing:

  • Test with Different Transactions:

    • Test your function with various transaction signatures to ensure it works across different types of transactions.
    • Use both simple transfers and more complex transactions.
  • Log Unexpected Instructions:

    • Modify the logging level to capture unexpected instruction formats.
    • This will help you adjust your parsing logic to handle more cases if needed.

Example of Enhanced Logging:

python
for instruction in instructions: program = instruction.get('program') or instruction.get('programId') if program == 'system' or program == '11111111111111111111111111111111': # Proceed with parsing else: logging.debug(f"Instruction with unhandled program: {program}, instruction: {instruction}")

Handling 'programId' as Base58 String:

  • If program is not equal to 'system', it might be the program ID in base58 format.

  • You can compare program to the system program ID:

    python
    SYSTEM_PROGRAM_ID = '11111111111111111111111111111111' program_id = instruction.get('programId') if program_id == SYSTEM_PROGRAM_ID: # Proceed with parsing

Final Code Example:

Combining the suggestions, here's a more robust version:

python
import aiohttp import logging SYSTEM_PROGRAM_ID = '11111111111111111111111111111111' async def get_sol_transaction(tx_signature: str) -> dict | None: # ... (previous code remains the same) ... # Инициализация переменных получателя и суммы receiver = None amount_lamports = None # Отправитель — первый в списке accountKeys (fee payer) sender = account_keys[0]['pubkey'] # Поиск инструкции перевода в списке инструкций for instruction in instructions: program = instruction.get('program') program_id = instruction.get('programId') if program == 'system' or program_id == SYSTEM_PROGRAM_ID: parsed = instruction.get('parsed') if parsed and parsed.get('type') == 'transfer': info = parsed.get('info', {}) receiver = info.get('destination') amount_lamports = int(info.get('lamports', 0)) break # Предполагаем, что нас интересует первая инструкция перевода else: logging.debug(f"Skipping instruction: {instruction}") if amount_lamports: amount_sol = amount_lamports / 1e9 else: amount_sol = None return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount": amount_sol, }

Summary:

  • Avoid KeyError by using .get() when accessing dictionary keys.
  • Check both 'program' and 'programId' to identify system program instructions.
  • Use logging to debug and understand the structure of different instructions.
  • Test your function thoroughly with different transaction types.

Additional Considerations:

  • Handle Transactions Without Transfers:

    • Some transactions might not involve SOL transfers but might interact with tokens or other programs.
    • Decide how you want to handle such cases—either skip them or extract relevant information.
  • Monitor API Limits:

    • Be aware of rate limits on the Solana RPC endpoint.
    • Implement retries with exponential backoff and handle HTTP 429 responses gracefully.

Final Thoughts:

By making these adjustments, your function should handle transactions more robustly and avoid crashing due to unexpected instruction formats. Always ensure that your code can handle various edge cases, especially when dealing with external data sources like blockchain APIs.


If you have any more questions or need further assistance, feel free to ask!

Câu hỏi

INFO:httpx:HTTP Request: POST https://api.mainnet-beta.solana.com "HTTP/1.1 200 OK"
ERROR:root:Транзакция не найдена или произошла ошибка.
ERROR:asyncio:Task exception was never retrieved
future: <Task finished name='Task-3' coro=<send_notifications() done, defined at /app/main.py:15> exception=TypeError("'NoneType' object is not subscriptable")>
Traceback (most recent call last):
File "/app/main.py", line 34, in send_notifications
text = f"Транзакция: {transaction['transaction']} \n"
~~~~~~~~~~~^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not subscriptable

Вот что произошло когда я свапнул Solana в WEN, но сообщение в боте все таки пришло. Их было 2 и выглядели они так:

  1. Транзакция: 5m7TxDNou1szUSavuup4beUhH77W6fXxaNsePbUnkQHRd882XbkCYc8mYJxrwLaNbG2xoDqronQJHAXoDCScpNmp
    Отправитель: Habp5bncMSsBC3vkChyebepym5dcTNRYeg2LVG464E96
    Получатель: GJ2RJuf8BFZZHU4EjoVT9Br4gMsmgMQaVLENimxjEYw
    Сумма: 1e-07 SOL
  2. Транзакция: 5YvRSsdeRcK2e7NKpatWmt9pna5ppogcTvXHCyP9dwtbKEP78mRMxUWef1Ma3h3EiPkB6vCgc978cXbYSDbFgCDC
    Отправитель: fLiPgg2yTvmgfhiPkKriAHkDmmXGP6CdeFX9UF5o7Zc
    Получатель: GJ2RJuf8BFZZHU4EjoVT9Br4gMsmgMQaVLENimxjEYw
    Сумма: 1e-07 SOL

Suy nghĩ

Диагностируя и решая

Пользователь описывает новый сбой, указывая на ошибку при отправке трансакции. Понимаю, что это может быть связано с адресом или сетевыми проблемами.

Ошибка при отправке уведомлений

Ищем причину

Пытаюсь понять причину ошибки, которая приводит к тому, что переменная transaction оказывается None. Это происходит при вызове функции get_sol_transaction, которая возвращает None, если транзакция не найдена или произошла ошибка.

Проверка иска.

Ищу причину сбоя

Я думаю, что сбой может быть связан с неудачной попыткой обмена токенов, так как функция get_sol_transaction может не уметь обрабатывать такие транзакции, не находя инструкцию 'transfer'.

Улучшение функции

Я вижу, что за функцию get_sol_transaction нужно добавить больше логирования. Это поможет, когда транзакции не находятся, и добавить обработку исключений для других проблем.

Trả lời

The error you're encountering is:

TypeError: 'NoneType' object is not subscriptable

This error occurs because your get_sol_transaction function is returning None, and your send_notifications function is trying to access it as if it were a dictionary. Specifically, the line:

python
text = f"Транзакция: {transaction['transaction']} \n"

fails because transaction is None, and you cannot subscript ([]) a NoneType object.

Understanding the Error:

  • get_sol_transaction Returns None: Your function returns None when it cannot find the transaction or when an error occurs.
  • No Check for None: In your send_notifications function, you proceed to use transaction without checking if it's None.
  • TypeError Occurs: Accessing transaction['transaction'] when transaction is None raises a TypeError.

Why Did This Happen?

When you swapped Solana for WEN, the transaction might not have the expected structure that your get_sol_transaction function can parse. It could be a more complex transaction involving token swaps, which may not include the standard 'transfer' instruction of the 'system' program.

Solution:

  1. Check for None in send_notifications:

    Modify your send_notifications function to handle cases where get_sol_transaction returns None.

    python
    async def send_notifications(bot: Bot, timeout: int): """ Function to send notifications. """ while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Failed to get transactions for wallet {wallet.wallet_hash} due to: {repr(e)}") continue # Skip to the next wallet diff = new_links - old_links for link in diff: transaction = await get_sol_transaction(link) if transaction is None: logging.error(f"Failed to retrieve transaction details for {link}. Skipping.") continue # Skip this transaction text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += f"Сумма: {transaction['amount']} SOL \n" await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

    Explanation:

    • Check if transaction is None: Before trying to access its keys, verify that transaction is not None.
    • Logging: If transaction is None, log an error message and continue to the next iteration.
  2. Enhance Logging in get_sol_transaction:

    Modify get_sol_transaction to include the transaction signature in the error message. This will help you identify which transactions are causing issues.

    python
    async def get_sol_transaction(tx_signature: str) -> dict | None: # ... previous code ... async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json() if 'result' not in response or response['result'] is None: logging.error(f"Transaction {tx_signature} not found or an error occurred.") return None # ... rest of your code ...
  3. Handle Complex Transactions:

    Your get_sol_transaction function might not be equipped to handle complex transactions like token swaps. These transactions may not include the standard 'transfer' instruction you're looking for. To handle this:

    • Check for Inner Instructions:

      Some transactions include inner instructions that contain the actual transfer details.

      python
      meta = result.get('meta', {}) inner_instructions = meta.get('innerInstructions', []) # Process inner instructions similar to the main instructions
    • Expand Instruction Parsing:

      Modify your function to parse different types of instructions and handle tokens other than SOL.

      python
      for instruction in instructions: program = instruction.get('program') or instruction.get('programId') if program in ['system', 'spl-token']: # Handle system transfers and token transfers
  4. Update get_sol_transaction to Be More Robust:

    Here's an updated version of get_sol_transaction that attempts to handle token transfers and includes additional error handling:

    python
    async def get_sol_transaction(tx_signature: str) -> dict | None: """ Получение информации о транзакции Solana по ее подписи. """ url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [ tx_signature, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 0 } ] } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json() if 'result' not in response or response['result'] is None: logging.error(f"Transaction {tx_signature} not found or an error occurred.") return None result = response['result'] transaction = result['transaction'] message = transaction['message'] account_keys = message['accountKeys'] instructions = message['instructions'] # Sender (fee payer) is the first in accountKeys sender = account_keys[0]['pubkey'] # Initialize variables receiver = None amount_lamports = None token = 'SOL' # Check both main and inner instructions all_instructions = instructions meta = result.get('meta', {}) inner_instructions = meta.get('innerInstructions', []) for inner in inner_instructions: all_instructions.extend(inner.get('instructions', [])) for instruction in all_instructions: parsed = instruction.get('parsed') if not parsed: continue info = parsed.get('info', {}) instruction_type = parsed.get('type') # Handle system transfer if instruction_type == 'transfer': receiver = info.get('destination') amount_lamports = int(info.get('lamports', 0)) token = 'SOL' break # Handle token transfer elif instruction_type == 'transferChecked': receiver = info.get('destination') amount_lamports = int(info.get('tokenAmount', {}).get('amount', 0)) decimals = int(info.get('tokenAmount', {}).get('decimals', 0)) token = info.get('tokenAmount', {}).get('uiAmountString', 'Unknown Token') amount_sol = amount_lamports / (10 ** decimals) break if amount_lamports is not None: if token == 'SOL': amount = amount_lamports / 1e9 else: amount = amount_sol else: amount = None return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount": amount, "token": token, }

    Explanation:

    • Inner Instructions: We now include inner instructions, which may contain the actual transfer details for token swaps.
    • Parsing Token Transfers: We handle transferChecked instructions, which are commonly used for token transfers.
    • Token Amounts: We calculate the amount based on the token's decimals.
    • Include Token Name: We retrieve the token's symbol or name to include in the message.
  5. Update Message Formatting:

    Adjust your message in send_notifications to include the token name:

    python
    text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += f"Сумма: {transaction['amount']} {transaction['token']} \n"
  6. Handle Transactions Without Amount:

    If amount is None, decide how you want to handle such transactions. You can choose to:

    • Skip the Transaction:

      python
      if transaction['amount'] is None: logging.info(f"No amount found for transaction {transaction['transaction']}. Skipping.") continue
    • Send a Different Message:

      python
      text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += "Сумма: Не удалось определить сумму или тип транзакции.\n"

Final Thoughts:

  • Error Handling: Always check for None or missing data before accessing dictionary keys.
  • Logging: Provide detailed logging to help diagnose issues when they occur.
  • Transaction Variability: Be aware that transactions can vary widely in structure, especially with token swaps and more complex operations.
  • Testing: Test your code with different types of transactions to ensure it handles various cases.

Example of the Updated send_notifications Function:

python
async def send_notifications(bot: Bot, timeout: int): while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Failed to get transactions for wallet {wallet.wallet_hash} due to: {repr(e)}") continue diff = new_links - old_links for link in diff: transaction = await get_sol_transaction(link) if transaction is None: logging.error(f"Failed to retrieve transaction details for {link}. Skipping.") continue text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += f"Сумма: {transaction['amount']} {transaction['token']} \n" await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

Summary:

  • Check for None Values: Always ensure you're not accessing attributes of None objects.
  • Handle Different Transaction Types: Adjust your parsing logic to handle various transaction types, including token transfers and swaps.
  • Improve Error Messages: Include more context in your error messages to aid in debugging.
  • Robust Code: Write your code to be robust against unexpected data structures.

By implementing these changes, your application should handle transactions more gracefully, and you should no longer encounter the TypeError due to NoneType objects.


If you need further assistance or have additional questions, feel free to ask!

Câu hỏi

Теперь аутпут вот такой:

  1. Транзакция: 3ua66VcYeHMoYyMvP4pRzcw8NXz2irAgSEX6YsjaEQLE7MMLKa4d2jQ5hAAvMHxwe287uS2Fkx6PrC4WztwBiWgG
    Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq
    Получатель: AFFBbhjp6RQtaobLizaRh4Su39GN5uCCRQY7v5LRbYGe
    Сумма: 0.00015 SOL
  2. Транзакция: 3DtMVrBVetkyq4JxNG7yUoGAbuiVSa4jUXPPbLxNGEPBZba644nUcsG8WvpDDpYRx2sMNoST52cjhk5cCDoS2gRY
    Отправитель: fLiPgg2yTvmgfhiPkKriAHkDmmXGP6CdeFX9UF5o7Zc
    Получатель: GJ2RJuf8BFZZHU4EjoVT9Br4gMsmgMQaVLENimxjEYw
    Сумма: 1e-07 SOL
    Мне кажется сумма во втором сообщении неправильная, как это исправить чтобы избежать дизинформации?

Suy nghĩ

Обнаружение несоответствия

Пользователь получает ошибку, связанную с неправильной суммой транзакции. Примечаю, что сумма в ответе 1e-07 SOL принимает значение 0.00015 SOL, что вызывает вероятность разночтений.

Изучаю причины. Уже понятно: сумма вторая транзакция показывает 1e-07 SOL.

Проверяю сумму перевода.

Проверяю перевод

Я разбираю числительное в двух транзакциях, чтобы понять, почему сумма во второй транзакции отображается как 1e-07 SOL. Это связано с неправильным преобразованием лямпортов в SOL или ошибкой в калькуляции.

Ищу порядок

Проверяю обработку перевода токенов, чтобы убедиться, что amount_lamports учитывается правильно и uiAmountString не отображает название токена.

Изучаю структуру транзакции

Анализирую, что uiAmountString неверно используется для имени токена. Структура Solana транзакции показывает, что это поле относится к сумме.

Ищу правильный подход

Я исследую способ извлечения количества и названия токена, сопоставляя адрес начинки с символом токена и анализируя известный список токенов.

Изучаю структуру данных

Теперь я разбираюсь с полем info в данных parsed. Обнаруживаю адрес tokenAmount, а также ресурсы и назначения для токенов. Это подчеркивает централизацию информации и упрощает порядок обработки данных.

Обрабатываю передачу токенов

Проверяю, если инструкция о передаче токенов, и извлекаю сумму и адрес выпуска токенов из информации.

Отображение адреса

Так как функция get_token_symbol отсутствует, я предлагаю отобразить адрес mint как токен, если функция для отображения адресов mint недоступна.

то Transfer Token

Проверяю точность

Проверяю, правильно ли рассчитывается сумма, и избегаю неправильного форматирования.

Проверяю и актуализирую код, чтобы автоматически извлекать и форматировать количество и адрес смещения.

Обновление функций

Рассматриваю обновленный код для функции get_sol_transaction, включая уточнение суммы и формата. Также добавляю функцию get_token_symbol(token_mint) для поиска символа токена.

Ищем решение

Предлагаю рассмотреть использование криптовалютного списка токенов на Solana, чтобы сопоставить адреса впуска токенов с их символами.

Загрузить и хранить

Окей, я анализирую, как загрузить список токенов и хранить их в словаре, используя asyncio и aiohttp для асинхронного выполнения.

Загрузка и обновление

Теперь я предлагаю загружать список токенов при запуске и хранить token_map как глобальную переменную, чтобы избавиться от асинхронности в get_token_symbol.

Настраиваю код для отображения
Я устанавливаю token = token_mint, чтобы показать адрес майнинга. Теперь он объяснит о связи майнинг-адресов с символами токенов и форматировании количества до шести знаков после запятой.

Объясняю пользователю, что сумма в втором переводе отображается как 1e-07 SOL из-за малого значения, и предлагаю отформатировать это в фиксированное количество знаков после запятой для более понятного отображения.

Trả lời

The issue you're encountering stems from the way small floating-point numbers are displayed in Python. When dealing with very small amounts, Python may represent them in scientific notation (e.g., 1e-07), which can be confusing or misleading in user-facing messages.

Additionally, there may be inaccuracies in how the amount is calculated and displayed for token transfers, especially if the transaction involves tokens other than SOL.

Here's how to fix it:

  1. Format the Amount Appropriately:

    To avoid displaying amounts in scientific notation, you can format the amount using string formatting to a fixed number of decimal places.

  2. Ensure Correct Amount Calculation for Token Transfers:

    For token transfers, you need to extract the amount and token information correctly. Token transactions often involve different decimals and require proper parsing to display accurate information.

  3. Map Token Mint Addresses to Token Symbols:

    To display the correct token symbol (e.g., "WEN" instead of the token mint address), you can use a token list to map mint addresses to their corresponding symbols.

Step-by-Step Solution:


1. Update the get_sol_transaction Function

Modify your get_sol_transaction function to handle token transfers correctly and format the amount properly.

python
import aiohttp import logging # Load the token list once at startup token_map = {} def load_token_list(): global token_map import requests token_list_url = "https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json" response = requests.get(token_list_url) token_list = response.json() tokens = token_list.get('tokens', []) token_map = {token['address']: token['symbol'] for token in tokens} def get_token_symbol(mint_address): return token_map.get(mint_address, mint_address) # Return the symbol or the mint address if not found # Call load_token_list() at startup load_token_list() async def get_sol_transaction(tx_signature: str) -> dict | None: """ Retrieve Solana transaction information by its signature. """ url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [ tx_signature, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 0 } ] } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json() if 'result' not in response or response['result'] is None: logging.error(f"Transaction {tx_signature} not found or an error occurred.") return None result = response['result'] transaction = result['transaction'] message = transaction['message'] account_keys = message['accountKeys'] instructions = message['instructions'] # Sender (fee payer) is the first in accountKeys sender = account_keys[0]['pubkey'] # Initialize variables receiver = None amount = None token = 'SOL' token_mint = None # Combine instructions and inner instructions all_instructions = instructions.copy() meta = result.get('meta', {}) inner_instructions = meta.get('innerInstructions', []) for inner in inner_instructions: all_instructions.extend(inner.get('instructions', [])) for instruction in all_instructions: parsed = instruction.get('parsed') if not parsed: continue info = parsed.get('info', {}) instruction_type = parsed.get('type') program = instruction.get('program') or instruction.get('programId') if (program == 'system' or program == '11111111111111111111111111111111') and instruction_type == 'transfer': # Handle SOL transfer receiver = info.get('destination') amount_lamports = int(info.get('lamports', 0)) amount = amount_lamports / 1e9 token = 'SOL' break elif (program == 'spl-token' or program == 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA') and instruction_type in ['transfer', 'transferChecked']: # Handle SPL token transfer receiver = info.get('destination') amount_raw = int(info.get('tokenAmount', {}).get('amount', 0)) decimals = int(info.get('tokenAmount', {}).get('decimals', 0)) amount = amount_raw / (10 ** decimals) token_mint = info.get('mint') token = get_token_symbol(token_mint) break else: continue if amount is not None: return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount": amount, "token": token, } else: logging.info(f"No transfer instruction found in transaction {tx_signature}") return None

Explanation:

  • Token Mapping:

    • load_token_list(): Loads the Solana token list once at startup and populates the token_map dictionary.
    • get_token_symbol(mint_address): Retrieves the token symbol from token_map based on the mint address.
  • Amount Calculation:

    • For SOL Transfers:
      • Amount is calculated by converting lamports to SOL (amount_lamports / 1e9).
    • For Token Transfers:
      • Amount is calculated using the token's decimals (amount_raw / (10 ** decimals)).
  • Instruction Parsing:

    • Checks both main and inner instructions to find the transfer instruction.
    • Handles both 'system' program (for SOL transfers) and 'spl-token' program (for token transfers).

2. Update Message Formatting in send_notifications

Modify the message formatting to display the amount with a fixed number of decimal places to prevent scientific notation.

python
async def send_notifications(bot: Bot, timeout: int): while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Failed to get transactions for wallet {wallet.wallet_hash} due to: {repr(e)}") continue diff = new_links - old_links for link in diff: transaction = await get_sol_transaction(link) if transaction is None: logging.error(f"Failed to retrieve transaction details for {link}. Skipping.") continue # Format the amount to avoid scientific notation amount_formatted = "{:.9f}".format(transaction['amount']).rstrip('0').rstrip('.') text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += f"Сумма: {amount_formatted} {transaction['token']} \n" await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

Explanation:

  • Formatting the Amount:
    • "{:.9f}".format(transaction['amount']): Formats the amount to 9 decimal places.
    • rstrip('0').rstrip('.'): Removes trailing zeros and any trailing decimal point if the number is an integer.
  • Example:
    • If the amount is 0.0000001, it will display as 0.0000001 instead of 1e-07.
    • If the amount is 1.0000000, it will display as 1.

3. Verify the Results

After making these changes, your bot messages should display the amount accurately and avoid scientific notation.

Example Messages:

  1. SOL Transfer:

    text
    Транзакция: 3ua66VcYeHMoYyMvP4pRzcw8NXz2irAgSEX6YsjaEQLE7MMLKa4d2jQ5hAAvMHxwe287uS2Fkx6PrC4WztwBiWgG Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq Получатель: AFFBbhjp6RQtaobLizaRh4Su39GN5uCCRQY7v5LRbYGe Сумма: 0.00015 SOL
  2. Token Transfer:

    text
    Транзакция: 3DtMVrBVetkyq4JxNG7yUoGAbuiVSa4jUXPPbLxNGEPBZba644nUcsG8WvpDDpYRx2sMNoST52cjhk5cCDoS2gRY Отправитель: fLiPgg2yTvmgfhiPkKriAHkDmmXGP6CdeFX9UF5o7Zc Получатель: GJ2RJuf8BFZZHU4EjoVT9Br4gMsmgMQaVLENimxjEYw Сумма: 0.0000001 WEN

Note:

  • The token symbol (e.g., "WEN") should now be displayed correctly if the token mint address is present in the token list.
  • The amount should be displayed without scientific notation and with appropriate decimal places.

4. Additional Considerations

Handling Missing Tokens:

  • If a token is not found in the token list, get_token_symbol will return the mint address.
  • You may want to handle this by indicating that the token is unknown or by adding it to your token list.

Caching the Token List:

  • Since the token list is loaded at startup, make sure your application reloads the token list if it runs for extended periods, or consider updating it periodically.

Error Handling:

  • Ensure that all potential exceptions are caught and logged appropriately to prevent the application from crashing.

Summary

  • Formatting Amounts: By formatting the amount with fixed decimal places, you avoid displaying confusing scientific notation for small amounts.
  • Correct Amount Calculation: Ensuring that amounts are calculated correctly for both SOL and token transfers prevents misinformation.
  • Token Symbols: Mapping token mint addresses to symbols allows users to understand which token is involved in the transaction.
  • Robust Parsing: Handling both main and inner instructions ensures that all relevant transfer information is captured.

Final Thoughts:

By implementing these changes, your bot should now accurately display transaction amounts and tokens, providing clear and reliable information to users. Always test your application with various transaction types to ensure it handles different scenarios correctly.

If you need further assistance or have additional questions, feel free to ask!

Câu hỏi

Traceback (most recent call last):
File "/app/main.py", line 64, in <module>
asyncio.run(load_token_list())
File "/usr/local/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/app/parsers/crypto/solana_check.py", line 86, in load_token_list
token_list = await response.json()
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/client_reqrep.py", line 1278, in json
raise ContentTypeError(
aiohttp.client_exceptions.ContentTypeError: 200, message='Attempt to decode JSON with unexpected mimetype: text/plain; charset=utf-8', url='https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json'

async def load_token_list():
global token_map
token_list_url = "https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json"
async with aiohttp.ClientSession() as session:
async with session.get(token_list_url) as response:
token_list = await response.json()
tokens = token_list.get('tokens', [])
token_map = {token['address']: token['symbol'] for token in tokens}

Suy nghĩ

Определяне на най-доброто решение

Разглеждам последния въпрос на потребителя и помощника, заедно с тяхното взаимодействие.

Обнаруживаю проблему

Пользователь сталкивается с ошибкой, когда пытается загрузить данные о жетоне с внешнего URL, и сервер возвращает неожиданный тип содержимого, что приводит к исключению.

Рассматриваю возможные решения

Пользователь пытается загрузить файл JSON с помощью aiohttp, но получает ошибку ContentTypeError из-за неподходящего content-type. Один из способов решения — игнорировать content-type и принудительно парсить тело ответа как JSON.

Понимание формата данных

Я разбираюсь, как изменить параметр content_type в aiohttp, чтобы игнорировать тип контента заголовка и успешно загрузить JSON данные для списка токенов Solana.

Встречаю проблему

Окей, я смотрю, что ошибка появляется из-за заголовка content-type 'text/plain; charset=utf-8', в то время как aiohttp ожидает 'application/json'.

Trả lời

The error you're encountering is due to a mismatch between the expected Content-Type header and the actual Content-Type of the response when using aiohttp to fetch the JSON file from the URL. Here's the specific error message:

aiohttp.client_exceptions.ContentTypeError: 200, message='Attempt to decode JSON with unexpected mimetype: text/plain; charset=utf-8', url='https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json'

Understanding the Error:

  • aiohttp Behavior: When you use await response.json(), aiohttp expects the Content-Type header of the response to be 'application/json' by default. If the response has a different Content-Type, it raises a ContentTypeError.

  • Actual Content-Type: The URL you're accessing returns a Content-Type of 'text/plain; charset=utf-8', not 'application/json'. This is common for raw files served from GitHub's raw content URLs.

Solution:

You can instruct aiohttp to ignore the Content-Type header by passing content_type=None to response.json(). This tells aiohttp to attempt to parse the response body as JSON regardless of the Content-Type header.

Modified load_token_list Function:

python
import aiohttp import logging token_map = {} async def load_token_list(): global token_map token_list_url = "https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json" async with aiohttp.ClientSession() as session: async with session.get(token_list_url) as response: # Pass content_type=None to ignore the Content-Type header token_list = await response.json(content_type=None) tokens = token_list.get('tokens', []) token_map = {token['address']: token['symbol'] for token in tokens}

Explanation:

  • Passing content_type=None: This parameter overrides the default behavior of aiohttp and tells it to parse the response as JSON regardless of the Content-Type header.

    python
    token_list = await response.json(content_type=None)
  • Why This Works: Since you know the content at the URL is valid JSON, it's safe to bypass the Content-Type check.

Alternative Solution:

If you prefer not to bypass the Content-Type check, you can read the response as text and then parse it using the json module:

python
import aiohttp import json token_map = {} async def load_token_list(): global token_map token_list_url = "https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json" async with aiohttp.ClientSession() as session: async with session.get(token_list_url) as response: text = await response.text() token_list = json.loads(text) tokens = token_list.get('tokens', []) token_map = {token['address']: token['symbol'] for token in tokens}

Explanation:

  • Reading as Text: await response.text() reads the response body as a text string.

  • Parsing JSON Manually: json.loads(text) parses the text string into a Python dictionary.

Additional Recommendations:

  1. Error Handling:

    Add exception handling to catch any potential errors during the network request or JSON parsing.

    python
    import aiohttp import json import logging token_map = {} async def load_token_list(): global token_map token_list_url = "https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json" try: async with aiohttp.ClientSession() as session: async with session.get(token_list_url) as response: if response.status != 200: logging.error(f"Failed to fetch token list. HTTP status code: {response.status}") return text = await response.text() token_list = json.loads(text) tokens = token_list.get('tokens', []) token_map = {token['address']: token['symbol'] for token in tokens} except Exception as e: logging.error(f"An error occurred while loading the token list: {e}")
  2. Asynchronous Execution:

    Ensure that load_token_list() is called in an asynchronous context since it's an async function.

    python
    import asyncio # Call this in your main coroutine or setup function asyncio.create_task(load_token_list())

    Or, if you're in an asynchronous context:

    python
    await load_token_list()
  3. Update Token List Periodically:

    If your application runs continuously, you might want to update the token list periodically to account for new tokens.

    python
    async def update_token_list_periodically(interval_hours=24): while True: await load_token_list() await asyncio.sleep(interval_hours * 3600) # Sleep for specified hours

    Start this coroutine in your main event loop:

    python
    asyncio.create_task(update_token_list_periodically())

Summary:

  • Problem: aiohttp raises ContentTypeError because the response Content-Type is 'text/plain', not 'application/json'.

  • Solution: Override the default behavior by passing content_type=None to response.json(), or read the response as text and parse it manually using the json module.

  • Implementation: Modify your load_token_list function accordingly.

Final Thoughts:

By making these changes, your application should successfully fetch and parse the token list from the provided URL without encountering the ContentTypeError. Always ensure to handle exceptions gracefully to make your application robust.


If you have any further questions or need additional assistance, feel free to ask!

Câu hỏi

Ты сделал обработку так, чтобы мне не приходили сообщения о токенах НЕ Solana или ты сделал так чтобы мне писалось в каком токене пришла транзакция? просто если первый вариант, то он мне отправил лишь одно сообщение о том что солана отправлена, а о том что что-то пришло мне уведомление не приходило

Suy nghĩ

Размышляю над обработкой транзакций

Определяю, что обработка направлена на фильтрацию транзакций только с SOL или уведомления о входящих токенах.

Обрабатываюся с особенностями

Оказывается, что после изменения функции, сообщения теперь содержат символ токена, который отправляется или получаем. Интересно, как это влияет на уведомления.

Разбираю детали

Пользователь сообщает о получении уведомлений только о переводах Solana, игнорируя другие токены. Это замедляет процесс уточнения, но помогает выяснить, как улучшить поддержку всех токенов.

Проверяю код

Я просматриваю код, чтобы понять, обрабатываются ли переводы токенов и SOL. Возможно, логика кода пропускает переводы, не связанные с SOL.

Отличая токены

Пользователь не получает сообщения о переводах токенов, только о SOL. Нужно разобраться, почему это происходит и как правильно обрабатывать переводы.

Проверяю уведомления

Я уточняю, что уведомления должны поступать для транзакций с SOL и другими токенами. Обратите внимание, что уведомления о ликеровке токенов SPL не поступают.

Разбираю возможные причины

Проблемы с уведомлениями о переводах токенов могут быть связаны с буквальным восприятием указаний или неправильным извлечением символа токена из списка.

Добавляю логирование

Добавляю logging.debug в get_sol_transaction для отслеживания token transfers и их прогрессированного состояния.

Проверяю обработку

Окей, давайте посмотрим: я уверяюсь, что функция handle_transactions корректно разбирает транзакции, а функция send_notifications отправляет уведомления только при наличии транзакций.

Проверяю транзакции

Пытаюсь понять, как ручной запрос транзакции с передачей токенов для get_sol_transaction работает. Затем убеждаюшись, что адрес эмиссии токена присутствует в token_map, и проверяя логи на ошибки.

Увлажняю параметры

Проверяю, как обрабатываются внутренние инструкции и необходимые параметры, такие как десятичные значения токенов, чтобы удостовериться, что они корректно участвуют в вычислениях.

Предлагаю разбор

Я описываю изменения в логике парсинга инструкций для выделения сведений о переносе SOL и SPL token, уточняя поля и их значения.

Trả lời

Здравствуйте,

В предыдущих ответах я предоставил изменения в коде, предназначенные для обработки как SOL, так и токеновых переводов. Цель состояла в том, чтобы вы получали уведомления о транзакциях, связанных с SOL и другими токенами, с указанием токена в сообщениях.

Из вашего описания следует, что вы получаете только уведомления о транзакциях SOL, но не получаете уведомлений о транзакциях с другими токенами. Давайте вместе выясним, почему уведомления о токеновых переводах не приходят.


Проверка и отладка кода

1. Проверка функции get_sol_transaction

В функции get_sol_transaction мы пытаемся обработать как SOL, так и токеновые переводы. Возможно, токеновые переводы не обнаруживаются из-за ошибок в логике разбора инструкций.

Добавим дополнительные логи для отладки:

python
import logging async def get_sol_transaction(tx_signature: str) -> dict | None: # ... существующий код ... # Инициализация переменных receiver = None amount = None token = 'SOL' token_mint = None # Объединение инструкций и внутренних инструкций all_instructions = instructions.copy() meta = result.get('meta', {}) inner_instructions = meta.get('innerInstructions', []) for inner in inner_instructions: all_instructions.extend(inner.get('instructions', [])) for instruction in all_instructions: parsed = instruction.get('parsed') if not parsed: logging.debug(f"Инструкция без поля 'parsed': {instruction}") continue info = parsed.get('info', {}) instruction_type = parsed.get('type') program = instruction.get('program') or instruction.get('programId') if (program == 'system' or program == '11111111111111111111111111111111') and instruction_type == 'transfer': # Обработка перевода SOL logging.debug(f"SOL перевод обнаружен в транзакции {tx_signature}") receiver = info.get('destination') amount_lamports = int(info.get('lamports', 0)) amount = amount_lamports / 1e9 token = 'SOL' break elif (program == 'spl-token' or program == 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA') and instruction_type in ['transfer', 'transferChecked']: # Обработка токенового перевода logging.debug(f"Токеновый перевод обнаружен в транзакции {tx_signature}") receiver = info.get('destination') sender = info.get('source') # Для токеновых переводов отправитель — 'source' amount_raw = int(info.get('tokenAmount', {}).get('amount', 0)) decimals = int(info.get('tokenAmount', {}).get('decimals', 0)) amount = amount_raw / (10 ** decimals) token_mint = info.get('mint') token = get_token_symbol(token_mint) break else: logging.debug(f"Необработанная инструкция в транзакции {tx_signature}: {instruction}") continue if amount is not None: logging.debug(f"Транзакция {tx_signature} успешно обработана: {amount} {token}") return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount": amount, "token": token, } else: logging.info(f"В транзакции {tx_signature} не найдено инструкции перевода") return None

2. Проверка функции send_notifications

Убедитесь, что функция send_notifications не пропускает токеновые транзакции:

python
if transaction is None: logging.error(f"Не удалось получить детали транзакции для {link}. Пропуск.") continue # Переходим к следующей транзакции

3. Тестирование с известными токеновыми транзакциями

Попробуйте вручную вызвать get_sol_transaction с подписью транзакции, которая содержит токеновый перевод, и проверьте вывод:

python
test_signature = '...подпись транзакции с токеновым переводом...' transaction = await get_sol_transaction(test_signature) print(transaction)

4. Проверка отображения токенов

Убедитесь, что адрес токена (mint address) присутствует в token_map. Если токен отсутствует в списке, функция get_token_symbol вернет адрес mint, и это может вызвать недопонимание.


Возможные причины и решения

1. Инструкции не обнаруживаются

  • Проблема: Токеновые переводы могут находиться во внутренних инструкциях (innerInstructions), и их нужно правильно объединить с основными инструкциями.

  • Решение: Убедитесь, что вы правильно объединяете все инструкции:

    python
    all_instructions = instructions.copy() meta = result.get('meta', {}) inner_instructions = meta.get('innerInstructions', []) for inner in inner_instructions: all_instructions.extend(inner.get('instructions', []))

2. Неправильный парсинг инструкций

  • Проблема: Возможно, инструкции не парсятся должным образом из-за различий в структуре токеновых переводов.

  • Решение: Убедитесь, что вы правильно извлекаете sender, receiver, amount и token из инструкции.

3. Отсутствие токена в token_map

  • Проблема: Если токен отсутствует в загруженном списке токенов, символ токена не будет найден.

  • Решение: Добавьте обработку случая, когда символ токена не найден:

    python
    def get_token_symbol(mint_address): return token_map.get(mint_address, 'Unknown Token')

4. Пропуск транзакций из-за отсутствия суммы

  • Проблема: Если amount не рассчитан, транзакция может быть пропущена.

  • Решение: Убедитесь, что ваша логика корректно рассчитывает amount для токеновых переводов.


Дополнительные рекомендации

1. Улучшите обработку ошибок

Добавьте больше логов, чтобы отслеживать процесс и видеть, где возникают проблемы.

2. Проверьте соответствие полей

В токеновых переводах отправитель может быть указан в поле source, а получатель в destination.

3. Обновите сообщение в боте

Убедитесь, что вы правильно отображаете информацию о токенах в сообщениях:

python
amount_formatted = "{:.9f}".format(transaction['amount']).rstrip('0').rstrip('.') text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += f"Сумма: {amount_formatted} {transaction['token']} \n"

Заключение

Ваш код должен обрабатывать как SOL, так и токеновые переводы, отправляя уведомления с указанием токена. Поскольку вы не получаете уведомления о токеновых переводах, вероятно, есть проблема в логике обработки этих транзакций.

Я рекомендую:

  • Добавить дополнительные логи для отладки.
  • Проверить правильность парсинга инструкций, особенно для токеновых переводов.
  • Убедиться, что адреса токенов правильно отображаются в символы токенов.
  • Тщательно протестировать код с различными типами транзакций.

После внесения этих изменений вы должны начать получать уведомления о токеновых переводах с указанием соответствующего токена.

Если после этих шагов проблема не решится, пожалуйста, предоставьте дополнительные логи или информацию, и я с радостью помогу вам дальше.


Если у вас есть дополнительные вопросы или вам нужна помощь, пожалуйста, дайте мне знать!

Câu hỏi

А что означает это условие?
elif (program == 'spl-token' or program == 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA') and (
instruction_type in ['transfer', 'transferChecked']):
receiver = info.get('destination')
amount_raw = int(info.get('tokenAmount', {}).get('amount', 0))
decimals = int(info.get('tokenAmount', {}).get('decimals', 0))
amount = amount_raw / (10 ** decimals)
token_mint = info.get('mint')
token = get_token_symbol(token_mint)
break

Suy nghĩ

Понимание условия

Читаю и разбираю состояние "elif", которое определяет передачу токенов. Условие проверяет, что программа соответствует "spl-token" или "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", и тип операции - "transfer" или "transferChecked".

Определяю условия

Проверяю условие для 'spl-token' или 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', чтобы убедиться, что операция передачи или проверенной передачи выполняется.

Объясняю условия

Я описываю условие, проверяющее транзакции Solana на токеновые переводы, объясняя каждую строку кода и его роль в этом процессе.

Раскрываю детали

Условие предполагает проверку типа программы и типа инструкции, чтобы подтвердить, что это токеновый перевод.

Проверяю тип перевода

Проверяю тип инструкции ('transfer' или 'transferChecked'), чтобы понять, токеновый перевод или проверка. Извлекаю данные: адрес получателя, количество токенов, их десятичные знаки, mint-токен и символ токена.

Понимаю сущность

Условие обнаруживает и обрабатывает инструкции переводов токенов через SPL Token, идентифицируя и извлекая исходную публичную программу для уведомления.

Trả lời

Здравствуйте,

Это условие в вашем коде предназначено для обнаружения и обработки токеновых переводов в транзакциях Solana. Давайте подробно разберём, что оно означает и как работает.


Условие:

python
elif (program == 'spl-token' or program == 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA') and ( instruction_type in ['transfer', 'transferChecked']): # Обработка токенового перевода receiver = info.get('destination') amount_raw = int(info.get('tokenAmount', {}).get('amount', 0)) decimals = int(info.get('tokenAmount', {}).get('decimals', 0)) amount = amount_raw / (10 ** decimals) token_mint = info.get('mint') token = get_token_symbol(token_mint) break

Объяснение условия:

  1. Проверка программы:

    python
    (program == 'spl-token' or program == 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA')
    • program: Это переменная, содержащая идентификатор программы, связанной с данной инструкцией в транзакции.
    • 'spl-token': Человекочитаемое имя программы SPL Token, используемой для работы с токенами в Solana.
    • 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA': Это публичный ключ (адрес) программы SPL Token в сети Solana Mainnet.
    • Проверка: Условие проверяет, что программа инструкции является программой SPL Token, используя либо её имя, либо публичный ключ.
  2. Проверка типа инструкции:

    python
    instruction_type in ['transfer', 'transferChecked']
    • instruction_type: Тип инструкции, описывающий, какое действие выполняется.
    • 'transfer' и 'transferChecked': Типы инструкций, используемые для перевода токенов.
      • 'transfer': Стандартный перевод токенов.
      • 'transferChecked': Перевод токенов с дополнительными проверками (например, проверка количества десятичных знаков).
    • Проверка: Условие проверяет, что тип инструкции соответствует одному из типов перевода токенов.
  3. Комбинированное условие:

    • Условие объединяет проверки программы и типа инструкции с помощью оператора and.
    • Это означает, что блок кода внутри условия выполнится только если:
      • Инструкция связана с программой SPL Token и
      • Тип инструкции является 'transfer' или 'transferChecked'.

Что происходит внутри блока кода:

  1. Получение адреса получателя:

    python
    receiver = info.get('destination')
    • Извлекаем из информации инструкции (info) адрес получателя токенов.
  2. Получение количества токенов:

    python
    amount_raw = int(info.get('tokenAmount', {}).get('amount', 0))
    • tokenAmount: Поле, содержащее информацию о количестве токенов.
    • amount: Сырое значение количества токенов (в минимальных единицах, без учёта десятичных знаков).
    • Извлекаем сырое количество токенов и приводим его к целому числу.
  3. Получение количества десятичных знаков:

    python
    decimals = int(info.get('tokenAmount', {}).get('decimals', 0))
    • decimals: Количество десятичных знаков у токена.
    • Это необходимо для корректного вычисления реального количества токенов.
  4. Вычисление реального количества токенов:

    python
    amount = amount_raw / (10 ** decimals)
    • Приводим сырое количество токенов к реальному значению, учитывая количество десятичных знаков.
  5. Получение адреса mint токена:

    python
    token_mint = info.get('mint')
    • mint: Адрес mint токена, уникально идентифицирующий тип токена.
  6. Получение символа токена:

    python
    token = get_token_symbol(token_mint)
    • Функция get_token_symbol преобразует адрес mint в символ токена (например, 'USDC', 'WEN' и т.д.).
  7. Прерывание цикла:

    python
    break
    • После успешного извлечения информации о токеновом переводе, выходим из цикла, так как нужная информация уже получена.

Зачем это нужно:

  • Обнаружение токеновых переводов: Это условие позволяет вашему боту обнаруживать транзакции, в которых происходит перевод токенов, а не только SOL.
  • Извлечение важной информации: Позволяет получить адреса отправителя и получателя, количество и тип токена, что необходимо для информативного уведомления пользователя.
  • Универсальность: Обрабатывая как SOL, так и другие токены, ваш бот становится более полезным и информативным для пользователей, позволяя отслеживать все виды переводов.

Контекст в вашем коде:

  • Общий алгоритм:

    • Цикл перебирает все инструкции в транзакции (включая внутренние инструкции).
    • Проверяет каждую инструкцию на соответствие определённым критериям (является ли она переводом SOL или токенов).
    • При обнаружении подходящей инструкции извлекает необходимые данные.
  • Ранее в коде вы уже обработали переводы SOL:

    python
    if (program == 'system' or program == '11111111111111111111111111111111') and instruction_type == 'transfer': # Обработка перевода SOL
  • Теперь вы обрабатываете токеновые переводы с помощью данного условия.


Важно обратить внимание:

  • Проверка двух вариантов программы:

    • Некоторые данные могут содержать имя программы в человекочитаемом виде ('spl-token'), а другие могут содержать её публичный ключ.
    • Проверяя оба варианта, вы гарантируете, что не пропустите инструкции токенового перевода.
  • Типы инструкций:

    • Инструкции типа 'transfer' и 'transferChecked' используются для токеновых переводов.
    • Обрабатывая оба типа, вы учитываете различные варианты транзакций.
  • Извлечение данных:

    • Использование метода get с указанием пустого словаря {} по умолчанию предотвращает ошибки, если соответствующие поля отсутствуют.

Пример использования:

Если ваш бот обнаружит транзакцию с токеновым переводом, например, перевод 100 USDC от одного пользователя другому, этот блок кода позволит:

  • Определить, что это токеновый перевод через программу SPL Token.
  • Извлечь адрес получателя.
  • Рассчитать количество токенов с учётом десятичных знаков.
  • Получить символ токена ('USDC') по адресу mint.
  • Сформировать информативное сообщение для пользователя с деталями транзакции.

Заключение:

Данное условие является ключевым для обнаружения и обработки токеновых переводов в транзакциях Solana. Оно позволяет вашему боту отслеживать не только переводы SOL, но и любых других токенов, обеспечивая пользователей полной информацией о движениях средств на их кошельках.


Если у вас возникли дополнительные вопросы или нужна помощь с другим фрагментом кода, пожалуйста, дайте знать, и я с радостью помогу вам!

Câu hỏi

Я только что свапнул 172 WEN в 0.00001 SOL, но от бота получил такое сообщение:
Транзакция: Drtwa7aDfyDs5jsU6dwx1CEoJzFDJNnBB3YjcvQR7Te6aRJ3YYXMi3gBB5EFuyNpHaA3xs8Mbgabp8f912Ytqff
Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq
Получатель: 3uXhfzhAZjRdFmr62r7ooKyGHkJqQMMkBZvKZsfCKPVo
Сумма: 0 None

Suy nghĩ

Рассматриваю проблему

Пользователь обменял 172 WEN на 0.00001 SOL, но получил сообщение от бота с указанием "0 None".

Рассматриваю проблему

Окей, давайте разберемся, почему бот показывает "Amount: 0 None". Возможно, проблема в парсинге токенов в транзакциях Solana.

Ищем причину ошибки

Окей, давайте разберемся: проблема может быть в нулевом значении amount или пустом token. Возможно, код не обрабатывает swap-транзакции корректно.

Ищу причину

Проверяю, почему функция get_sol_transaction возвращает "Amount: 0 None" при разборах сделок обмена. Это связано с отсутствием данных о сумме и токене.

Уточняю детали

И я объясняю, почему сумма отображается как 0 и токен как None, а затем разбираюсь в транзакциях swap на Solana и предлагаю обновить функцию get_sol_transaction для правильного выделения swap транзакций.

Определяю трудности

Свап-транзакции сложны: они обычно требуют взаимодействия с смарт-контрактами, такими как обменники, и не включают команды transfer или transferChecked, ранее обрабатываемые в коде.

Ищу лучшее решение

Сейчас я разбираюсь с транзакциями и замечаю, что информация о переводах может быть скрыта в других инструкциях или метаданных. Это подсказывает, что нужна более глубокая проверка.

Уменьшаю число операций

Управление функцией, чтобы уменьшить количество операций, обращаясь ко всем инструкциям.

Инициализация и сопоставление

Инициализирую переменные, сопоставляю пред и пост балансы, вычисляю изменения.

Учитываю детали

Обрабатываю смарт-контракты, вычисляю изменения балансов и формирую сообщение на основе этих изменений.

Обновляем логику

Функция send_notifications теперь возвращает amount_info, обновляя сообщение бота. Учёт изменений балансов перед и после транзакции уточняет отправителя и получателя.

Обновление кода
Использование preTokenBalances и postTokenBalances для отслеживания изменений баланса и получения информации о токенах.

Trả lời

Здравствуйте,

Я понимаю, что вы только что совершили свап 172 WEN в 0.00001 SOL, но получили от бота сообщение:

text
Транзакция: Drtwa7aDfyDs5jsU6dwx1CEoJzFDJNnBB3YjcvQR7Te6aRJ3YYXMi3gBB5EFuyNpHaA3xs8Mbgabp8f912Ytqff Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq Получатель: 3uXhfzhAZjRdFmr62r7ooKyGHkJqQMMkBZvKZsfCKPVo Сумма: 0 None

Причина проблемы:

Ваш бот отображает сумму как 0 None потому что текущая логика парсинга транзакции не учитывает особенности свап-транзакций. Свап-транзакции сложнее, чем простые переводы, и не содержат явных инструкций transfer или transferChecked, которые ваш код ищет для определения суммы и токена.

Как исправить:

Чтобы корректно обрабатывать свап-транзакции и отображать информацию о полученных и отправленных токенах, необходимо использовать поля preTokenBalances и postTokenBalances из метаданных транзакции. Эти поля содержат информацию о балансах токенов до и после транзакции, что позволяет вычислить изменения в балансах и определить, какие токены были отправлены или получены.

Обновленный код функции get_sol_transaction:

python
import aiohttp import logging # Предполагается, что функция get_token_symbol инициализирована и доступна # Также token_map должен быть загружен и доступен async def get_sol_transaction(tx_signature: str) -> dict | None: """ Получение информации о транзакции Solana по ее подписи. """ url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [ tx_signature, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 0 } ] } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json(content_type=None) if 'result' not in response or response['result'] is None: logging.error(f"Транзакция {tx_signature} не найдена или произошла ошибка.") return None result = response['result'] transaction = result['transaction'] message = transaction['message'] account_keys = message['accountKeys'] sender = account_keys[0]['pubkey'] meta = result.get('meta', {}) pre_token_balances = meta.get('preTokenBalances', []) post_token_balances = meta.get('postTokenBalances', []) # Инициализируем словарь для хранения изменений балансов balance_changes = {} # Обрабатываем балансы токенов for pre_balance, post_balance in zip(pre_token_balances, post_token_balances): mint = pre_balance.get('mint') owner = pre_balance.get('owner') pre_amount = int(pre_balance.get('uiTokenAmount', {}).get('amount', 0)) pre_decimals = int(pre_balance.get('uiTokenAmount', {}).get('decimals', 0)) post_amount = int(post_balance.get('uiTokenAmount', {}).get('amount', 0)) # Предполагаем, что decimals одинаковы в pre и post amount_change = (post_amount - pre_amount) / (10 ** pre_decimals) if amount_change != 0: token_symbol = get_token_symbol(mint) balance_changes[token_symbol] = balance_changes.get(token_symbol, 0) + amount_change # Также обрабатываем изменения SOL баланса pre_balances = meta.get('preBalances', []) post_balances = meta.get('postBalances', []) sol_changes = {} for idx, (pre_balance, post_balance) in enumerate(zip(pre_balances, post_balances)): amount_change = (post_balance - pre_balance) / 1e9 # Преобразуем лампорты в SOL if amount_change != 0: pubkey = account_keys[idx]['pubkey'] if pubkey == sender: sol_changes['SOL'] = sol_changes.get('SOL', 0) + amount_change # Объединяем изменения токенов и SOL all_changes = {**balance_changes, **sol_changes} if all_changes: amount_info = '' for token_symbol, amount in all_changes.items(): # Форматируем сумму amount_formatted = "{:.9f}".format(abs(amount)).rstrip('0').rstrip('.') action = 'Получено' if amount > 0 else 'Отправлено' amount_info += f"{action}: {amount_formatted} {token_symbol}\n" # Получатель может быть неочевиден в свап-транзакциях receiver = 'N/A' return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount_info": amount_info, } else: logging.info(f"Не удалось определить изменения баланса в транзакции {tx_signature}") return None

Обновите функцию send_notifications:

python
async def send_notifications(bot: Bot, timeout: int): while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Failed to get transactions for wallet {wallet.wallet_hash} due to: {repr(e)}") continue diff = new_links - old_links for link in diff: transaction = await get_sol_transaction(link) if transaction is None: logging.error(f"Failed to retrieve transaction details for {link}. Skipping.") continue text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += transaction['amount_info'] await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

Пояснение изменений:

  • Использование preTokenBalances и postTokenBalances: Это позволяет определить изменения в балансах токенов, включая свапы.

  • Обработка изменений SOL баланса: Мы также учитываем изменения баланса SOL через preBalances и postBalances.

  • Формирование amount_info: Собираем информацию о всех изменениях балансов токенов и SOL, чтобы отобразить их в сообщении.

  • Корректное отображение суммы и токена: Теперь бот должен отображать правильные суммы и названия токенов, даже для свап-транзакций.

Пример ожидаемого сообщения от бота:

text
Транзакция: Drtwa7aDfyDs5jsU6dwx1CEoJzFDJNnBB3YjcvQR7Te6aRJ3YYXMi3gBB5EFuyNpHaA3xs8Mbgabp8f912Ytqff Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq Получатель: N/A Отправлено: 172 WEN Получено: 0.00001 SOL

Дополнительные рекомендации:

  • Улучшение определения получателя: В свап-транзакциях получатель может быть неочевиден. Если это важно, можно попытаться определить получателя на основе инструкций или метаданных.

  • Обработка отрицательных значений: Убедитесь, что отображаете корректное действие ("Получено" или "Отправлено") в зависимости от знака изменения баланса.

  • Тестирование: Протестируйте бота с разными типами транзакций, включая свапы и обычные переводы, чтобы убедиться, что информация отображается корректно.

Заключение:

  • Причина проблемы: Текущая реализация не учитывает особенности свап-транзакций, поэтому бот не может правильно определить сумму и токен.

  • Решение: Использование балансов до и после транзакции для вычисления изменений и формирования корректной информации.

Если у вас возникнут дополнительные вопросы или потребуется помощь с реализацией этих изменений, пожалуйста, дайте знать. Я буду рад помочь вам настроить бота для корректного отображения информации о всех транзакциях.

Câu hỏi

Я свапнул 25.68584 WEN на 0.000015 SOL, но во входящем сообщении мало того что получил очень странный ответ, так еще и ни одна из сумм по итогу не правильная
Транзакция: 5iYVXEQ7bFQcveU36uwbXkXtMTMqHas3iG2PD8B6LR4dk54sy36Y3twZAVErYFJN5QHcdno1YajTf4yd9GmYjwRy
Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq
Получатель: N/A
Отправлено: 0 Unknown Token
Отправлено: 0.00039 SOL

Suy nghĩ

Ищу несоответствия

Использую транзакцию и просмотр сообщений, чтобы понять разницу в отправленных и полученных токенах.

Проверяю транзакции

Я разбираюсь в обмене 25.68584 WEN на 0.000015 SOL и замечаю, что бот отправляет сообщение с неправильными суммами: указан 0 Unknown Token и 0.00039 SOL.

Анализируя проблемы

Подтверждаю возможные ошибки в вычислении суммы, маппинге адресов токенов и неправильных индексах данных. В прямом смысле, пошагово разбираю функцию get_sol_transaction.

Обрабатываю транзакции

Анализирую изменения в балансе токенов и SOL, рассматривая pre и post балансы, чтобы определить сумму изменений.

Уточняю детали

Сейчас я разбираюсь в расчете изменения баланса токенов, сопоставляя предпочтительные и пост-транзакционные балансы. Это наводит на мысль о необходимости проверки совпадения владельцев и адресовMint.

Уточняю данные

Я замечаю, что в коде не учитывается изменение баланса SOL у получателя и налоговые вычеты, а также значения не совсем соответствуют текущей информации о транзакции.

Выявляю изменения

Я разбираюсь, как можно вычислить изменения баланса не только отправителя, но и всех аккаунтов, чтобы понять чистые изменения в кошельке пользователя.

Изучение запросов

Использую aiohttp для формирования и отправки HTTP POST-запроса к Solana API, передавая параметры для получения информации о транзакции по подписи.

Отслеживание транзакций

Я работаю над функцией, которая отслеживает изменения баланса для кошелька пользователя в блокчейне. Это включает проверку предварительных и посттранзакционных состояний, конвертацию и форматирование изменений в пределах заданного диапазона.

Определяю изменения

Использую accountIndex для получения публичного ключа и сверяю его с user_wallet. Затем вычисляю изменения в балансе пользователя и учитываю комиссии за транзакции.

Рассматриваю балансовые изменения

Я думаю о том, как включить изменения балансов в кошельке после каждого обмена. Интересно, какие данные возвращают preTokenBalances и postTokenBalances.

Trả lời

Здравствуйте,

Я понимаю вашу проблему: после свапа 25.68584 WEN на 0.000015 SOL ваш бот прислал сообщение с некорректными суммами:

text
Транзакция: 5iYVXEQ7bFQcveU36uwbXkXtMTMqHas3iG2PD8B6LR4dk54sy36Y3twZAVErYFJN5QHcdno1YajTf4yd9GmYjwRy Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq Получатель: N/A Отправлено: 0 Unknown Token Отправлено: 0.00039 SOL

Проблема:

  • Некорректные суммы: Отображаются неверные суммы и токен отображается как "Unknown Token".
  • Причина: Код неправильно вычисляет изменения балансов, не учитывая конкретно ваш кошелёк, и, возможно, включает балансы других участников транзакции.

Решение:

Чтобы корректно вычислить изменения баланса для вашего кошелька, нам нужно:

  1. Фильтровать изменения баланса по вашему кошельку. Убедиться, что мы учитываем только те изменения, которые относятся к вашему адресу.

  2. Исправить логику расчёта балансов. Убедиться, что мы правильно сопоставляем preTokenBalances и postTokenBalances и учитываем accountIndex для сопоставления с вашим кошельком.

  3. Обработать все балансы. Включая SOL и токены, а также корректно рассчитывать изменения, учитывая возможные комиссии.


Обновлённый код функции get_sol_transaction:

python
async def get_sol_transaction(tx_signature: str, user_wallet: str) -> dict | None: """ Получение информации о транзакции Solana по ее подписи. """ url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [ tx_signature, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 0 } ] } async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json(content_type=None) if 'result' not in response or response['result'] is None: logging.error(f"Транзакция {tx_signature} не найдена или произошла ошибка.") return None result = response['result'] transaction = result['transaction'] message = transaction['message'] account_keys = message['accountKeys'] account_pubkeys = [account['pubkey'] for account in account_keys] meta = result.get('meta', {}) pre_token_balances = meta.get('preTokenBalances', []) post_token_balances = meta.get('postTokenBalances', []) balance_changes = {} # Обрабатываем токеновые балансы token_balances = {} for pre_balance in pre_token_balances: account_index = pre_balance.get('accountIndex') pubkey = account_pubkeys[account_index] if pubkey != user_wallet: continue # Пропускаем чужие кошельки mint = pre_balance.get('mint') decimals = int(pre_balance.get('uiTokenAmount', {}).get('decimals', 0)) pre_amount = int(pre_balance.get('uiTokenAmount', {}).get('amount', 0)) token_balances[mint] = {'pre_amount': pre_amount, 'decimals': decimals} for post_balance in post_token_balances: account_index = post_balance.get('accountIndex') pubkey = account_pubkeys[account_index] if pubkey != user_wallet: continue mint = post_balance.get('mint') decimals = int(post_balance.get('uiTokenAmount', {}).get('decimals', 0)) post_amount = int(post_balance.get('uiTokenAmount', {}).get('amount', 0)) pre_amount = token_balances.get(mint, {}).get('pre_amount', 0) amount_change = (post_amount - pre_amount) / (10 ** decimals) if amount_change != 0: token_symbol = get_token_symbol(mint) balance_changes[token_symbol] = balance_changes.get(token_symbol, 0) + amount_change # Обрабатываем изменения SOL баланса pre_balances = meta.get('preBalances', []) post_balances = meta.get('postBalances', []) for idx, (pre_balance, post_balance) in enumerate(zip(pre_balances, post_balances)): pubkey = account_pubkeys[idx] if pubkey != user_wallet: continue amount_change = (post_balance - pre_balance) / 1e9 # Лампорты в SOL if amount_change != 0: balance_changes['SOL'] = balance_changes.get('SOL', 0) + amount_change if balance_changes: amount_info = '' for token_symbol, amount in balance_changes.items(): # Форматируем сумму amount_formatted = "{:.9f}".format(abs(amount)).rstrip('0').rstrip('.') action = 'Получено' if amount > 0 else 'Отправлено' amount_info += f"{action}: {amount_formatted} {token_symbol}\n" return { "transaction": tx_signature, "sender": user_wallet, "receiver": 'N/A', "amount_info": amount_info, } else: logging.info(f"Не удалось определить изменения баланса в транзакции {tx_signature}") return None

Объяснение изменений:

  • Передача адреса пользователя: Теперь функция принимает параметр user_wallet, чтобы фильтровать изменения балансов по вашему кошельку.

  • Фильтрация по accountIndex: Используем accountIndex из preTokenBalances и postTokenBalances для получения соответствующего публичного ключа и сравниваем с user_wallet.

  • Расчёт изменений балансов: Вычисляем изменения балансов только для вашего кошелька, что предотвращает включение данных других участников транзакции.

  • Корректное отображение сумм и токенов: Теперь бот должен правильно отображать полученные и отправленные суммы, соответствующие вашему кошельку.


Обновление функции send_notifications:

Необходимо передать wallet.wallet_hash в функцию get_sol_transaction:

python
transaction = await get_sol_transaction(link, wallet.wallet_hash)

Проверка и тестирование:

  1. Тестирование с вашей транзакцией:

    Используя обновлённый код и вашу транзакцию 5iYVXEQ7bFQcveU36uwbXkXtMTMqHas3iG2PD8B6LR4dk54sy36Y3twZAVErYFJN5QHcdno1YajTf4yd9GmYjwRy, бот должен отобразить:

    text
    Транзакция: 5iYVXEQ7bFQcveU36uwbXkXtMTMqHas3iG2PD8B6LR4dk54sy36Y3twZAVErYFJN5QHcdno1YajTf4yd9GmYjwRy Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq Получатель: N/A Отправлено: 25.68584 WEN Получено: 0.000015 SOL

    Обратите внимание: Суммы могут немного отличаться из-за комиссий.

  2. Проверка обработки комиссий:

    • Учитывайте, что при свапах и других транзакциях могут быть комиссии, которые также влияют на баланс.
    • В SOL балансах изменение может быть отрицательным, если вы платите комиссию.
  3. Обработка токенов с неизвестными символами:

    • Если get_token_symbol не находит символ токена, убедитесь, что функция возвращает осмысленное значение, например, "Unknown Token" или адрес mint.

Дополнительные рекомендации:

  • Улучшите функцию get_token_symbol:

    python
    def get_token_symbol(mint_address): symbol = token_map.get(mint_address) if symbol: return symbol else: # Логирование неизвестного токена logging.warning(f"Неизвестный токен с mint адресом: {mint_address}") return mint_address # Или 'Unknown Token'
  • Обработка небольших изменений балансов:

    • Если изменение баланса очень маленькое (например, комиссии), вы можете настроить порог отображения или помечать такие изменения как комиссии.
  • Добавьте информацию о комиссии:

    • Вы можете вывести информацию о комиссионных расходах, используя поле fee из метаданных транзакции:

      python
      fee = meta.get('fee', 0) / 1e9 # Преобразуем из лампортов в SOL if fee > 0: amount_info += f"Комиссия: {fee} SOL\n"

Заключение:

  • Причина проблемы: Код не учитывал ваш конкретный кошелёк при расчёте изменений балансов, из-за чего суммы отображались некорректно.

  • Решение: Обновить функцию get_sol_transaction, чтобы она фильтровала изменения балансов по вашему кошельку и корректно вычисляла суммы.

  • Результат: Бот теперь будет правильно отображать суммы отправленных и полученных токенов и SOL, соответствующих вашим транзакциям.


Если после внесения этих изменений у вас всё ещё возникают проблемы или есть дополнительные вопросы, пожалуйста, дайте мне знать, и я с радостью помогу вам дальше!

Câu hỏi

Какие конкретно изменения мне нужно внести в send_notifications? сейчас она выглядит вот так:
async def send_notifications(bot: Bot, timeout: int):
while True:
wallets = await PostgresRequests().get_all_wallets()
for wallet in wallets:
old_links = await get_links(wallet_hash=wallet.wallet_hash)
try:
new_links = set(await get_latest_sol_transactions(wallet.wallet_hash))
except Exception as e:
logging.error(f"Failed to get transactions for wallet {wallet.wallet_hash} due to: {repr(e)}")
continue
diff = new_links - old_links
for link in diff:
transaction = await get_sol_transaction(link)
if transaction is None:
logging.error(f"Failed to retrieve transaction details for {link}. Skipping.")
continue
text = f"Транзакция: {transaction['transaction']} \n"
text += f"Отправитель: {transaction['sender']} \n"
text += f"Получатель: {transaction['receiver']} \n"
text += transaction['amount_info']
await bot.send_message(chat_id=wallet.tg_id, text=text)
await store_links(wallet_hash=wallet.wallet_hash, links=new_links)
await asyncio.sleep(timeout)

Suy nghĩ

Обновляю и фильтрую

Периодически проверяю все кошельки, обновляю ссылки и фильтрую балансы. Ошибки при получении транзакций фиксирую и проигнорировываю.

Обновление функции уведомлений

Следует внести изменения в функцию send_notifications, чтобы передать аргументом wallet.wallet_hash при вызове get_sol_transaction.

Управление уведомлениями

Я занимаюсь обновлением функции отправки уведомлений. Обрабатываю кошельки, извлекаю новые ссылки и транзакции, отправляю сообщения и сохраняю ссылки.

Trả lời

Здравствуйте,

Вам нужно внести небольшое изменение в функцию send_notifications, чтобы она передавала адрес кошелька пользователя в функцию get_sol_transaction, которая теперь требует два аргумента: подпись транзакции (tx_signature) и адрес кошелька пользователя (user_wallet).

Вот конкретные изменения, которые вам нужно внести:

  1. Изменить вызов get_sol_transaction внутри цикла по транзакциям:

    Было:

    python
    transaction = await get_sol_transaction(link)

    Стало:

    python
    transaction = await get_sol_transaction(link, wallet.wallet_hash)

Полный обновлённый код функции send_notifications:

python
async def send_notifications(bot: Bot, timeout: int): while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Failed to get transactions for wallet {wallet.wallet_hash} due to: {repr(e)}") continue diff = new_links - old_links for link in diff: # Передаём wallet.wallet_hash в функцию get_sol_transaction transaction = await get_sol_transaction(link, wallet.wallet_hash) if transaction is None: logging.error(f"Failed to retrieve transaction details for {link}. Skipping.") continue text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += transaction['amount_info'] await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

Пояснение изменений:

  • Передача адреса кошелька:

    • Теперь функция get_sol_transaction ожидает два аргумента:
      • tx_signature: подпись транзакции (в вашем коде это link).
      • user_wallet: адрес кошелька пользователя (в вашем коде это wallet.wallet_hash).
    • Поэтому мы обновили вызов функции, добавив wallet.wallet_hash как второй аргумент.
  • Почему это важно:

    • В предыдущих изменениях мы обновили get_sol_transaction для корректного вычисления изменений балансов, относящихся только к конкретному кошельку пользователя.
    • Передавая wallet.wallet_hash, функция может фильтровать изменения балансов и отображать только релевантную информацию для каждого пользователя.

Убедитесь, что ваша функция get_sol_transaction обновлена и выглядит следующим образом:

python
async def get_sol_transaction(tx_signature: str, user_wallet: str) -> dict | None: # Ваш обновлённый код функции здесь...

Дополнительные рекомендации:

  • Проверьте, что все необходимые импорты и переменные определены:

    • Убедитесь, что wallet.wallet_hash содержит правильный адрес кошелька в виде строки.
    • Проверьте, что wallet.tg_id содержит идентификатор чата Telegram для отправки сообщений.
  • Тестирование:

    • После внесения изменений протестируйте бота, выполнив транзакцию, и убедитесь, что уведомление содержит корректную информацию о суммах и токенах.
  • Логирование:

    • Если возникают ошибки или неожиданные результаты, проверьте логи приложения на наличие сообщений об ошибках.
    • Убедитесь, что все ошибки обрабатываются корректно и приложение не падает.

Пример ожидаемого результата:

После внесения изменений и выполнения транзакции бот должен отправить сообщение, которое точно отражает изменения баланса для конкретного пользователя.

Пример сообщения:

text
Транзакция: Drtwa7aDfyDs5jsU6dwx1CEoJzFDJNnBB3YjcvQR7Te6aRJ3YYXMi3gBB5EFuyNpHaA3xs8Mbgabp8f912Ytqff Отправитель: 76a3CxHZYeGoTJLXopkKqE53zMBwgNriDyDgPZ456gmq Получатель: N/A Отправлено: 25.68584 WEN Получено: 0.000015 SOL

Если у вас возникают проблемы после внесения изменений, убедитесь, что:

  • Функция get_sol_transaction корректно фильтрует изменения балансов по адресу пользователя.
  • Вызов функции get_sol_transaction в send_notifications передаёт правильные аргументы.
  • Все асинхронные функции (async def) вызываются с использованием await.

Заключение:

  • Конкретное изменение: Добавьте wallet.wallet_hash в вызов get_sol_transaction внутри функции send_notifications.
  • Цель изменения: Обеспечить, чтобы функция get_sol_transaction получала адрес кошелька пользователя и могла корректно вычислять изменения балансов, относящиеся к этому кошельку.
  • Результат: Бот будет отправлять уведомления с точной информацией о транзакциях, соответствующих каждому пользователю.

Если у вас есть дополнительные вопросы или нужна помощь с другими частями кода, пожалуйста, дайте знать, и я с радостью помогу вам!

Câu hỏi

Перепиши метод парсинга транзакций так, чтобы он парсил все те же данные через API Solscan, используя подписи транзакций. Также распиши какие изменения надо сделать в других функциях по типу send_notifications

Suy nghĩ

Переписывая методы

Пользователь интересуется, как переписать метод парсинга транзакций через Solscan API, и что изменить в других функциях, например, send_notifications.

Изучая Solscan API

Использование Solscan API для получения деталей транзакций требует адаптации функции get_sol_transaction. Важные изменения включают доставку данных о транзакции.

Создаю функцию

Пишу функцию get_sol_transaction, используя API Solscan для сбора данных о транзакции: подпись, отправитель, получатель, сумма, токен и т.д. Adjustирую функцию для интеграции с Solana RPC API.

Изучаю API транзакций

Я смотрю на функцию get_sol_transaction, которая извлекает данные о транзакциях из Solscan API, используя подпись транзакции и адрес кошелька пользователя.

Извлечение данных из ответа API

Отслеживание транзакций

Проверяю транзакции на сайте Solscan, чтобы убедиться, что кошелек пользователя участвует в операциях, и собираю сводку деталей транзакции.

Trả lời

Здравствуйте,

Вы хотите переписать метод парсинга транзакций так, чтобы он использовал API Solscan для получения деталей транзакций по их подписи. Также вы просите объяснить, какие изменения нужно внести в другие функции, такие как send_notifications.


1. Использование API Solscan для получения информации о транзакции

Solscan предоставляет публичный API, который позволяет получать детали транзакций по их подписи. Нам нужно переписать функцию get_sol_transaction, чтобы она использовала этот API.

Шаги:

  1. Изучить документацию API Solscan

    API для получения деталей транзакции:

    GET https://public-api.solscan.io/transaction/{txHash}
    

    Где {txHash} — это подпись транзакции.

  2. Обновить функцию get_sol_transaction

    Перепишем функцию так, чтобы она делала GET-запрос к API Solscan и парсила необходимые данные.


Новая реализация функции get_sol_transaction

python
import aiohttp import logging # Предполагается, что функция get_token_symbol и token_map уже определены async def get_sol_transaction(tx_signature: str, user_wallet: str) -> dict | None: """ Получение информации о транзакции через API Solscan по ее подписи. :param tx_signature: Подпись транзакции в виде строки. :param user_wallet: Адрес кошелька пользователя. :return: Словарь с информацией о транзакции или None в случае ошибки. """ url = f'https://public-api.solscan.io/transaction/{tx_signature}' headers = { 'accept': 'application/json' } try: async with aiohttp.ClientSession() as session: async with session.get(url, headers=headers) as resp: if resp.status != 200: logging.error(f"Не удалось получить данные транзакции {tx_signature}. Код ответа: {resp.status}") return None response = await resp.json() # Проверяем наличие необходимых данных if not response: logging.error(f"Пустой ответ для транзакции {tx_signature}") return None # Инициализация переменных sender = user_wallet receiver = 'N/A' amount_info = '' # Получаем список инструкций instructions = response.get('parsedInstruction', []) if not instructions: logging.info(f"Нет инструкций в транзакции {tx_signature}") return None # Обрабатываем инструкции for instruction in instructions: instruction_type = instruction.get('type') info = instruction.get('data', {}) if instruction_type == 'transfer': # Обработка перевода SOL source = info.get('source') destination = info.get('destination') lamports = int(info.get('lamports', 0)) amount = lamports / 1e9 # Преобразуем лампорты в SOL token = 'SOL' if source == user_wallet or destination == user_wallet: amount_formatted = "{:.9f}".format(abs(amount)).rstrip('0').rstrip('.') action = 'Получено' if destination == user_wallet else 'Отправлено' amount_info += f"{action}: {amount_formatted} {token}\n" elif instruction_type in ['transferChecked', 'transfer']: # Обработка перевода токенов source = info.get('source') destination = info.get('destination') mint = info.get('mint') decimals = int(info.get('tokenAmount', {}).get('decimals', 0)) amount_raw = int(info.get('tokenAmount', {}).get('amount', 0)) amount = amount_raw / (10 ** decimals) token = get_token_symbol(mint) if source == user_wallet or destination == user_wallet: amount_formatted = "{:.9f}".format(abs(amount)).rstrip('0').rstrip('.') action = 'Получено' if destination == user_wallet else 'Отправлено' amount_info += f"{action}: {amount_formatted} {token}\n" # Если нет релевантных изменений баланса if not amount_info: logging.info(f"Нет изменений баланса для транзакции {tx_signature} и кошелька {user_wallet}") return None return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount_info": amount_info, } except Exception as e: logging.error(f"Произошла ошибка при получении транзакции {tx_signature}: {e}") return None

Пояснения:

  • Запрос к API Solscan:

    • Используем aiohttp для выполнения асинхронного GET-запроса к API Solscan.
    • Проверяем статус ответа и парсим JSON.
  • Парсинг ответа:

    • Получаем список инструкций из поля parsedInstruction.
    • Проходим по каждой инструкции и ищем те, которые относятся к переводу SOL или токенов.
    • Проверяем, участвует ли кошелёк пользователя в инструкции (source или destination).
    • Извлекаем необходимую информацию: сумму, токен, действие (получено или отправлено).
  • Обработка ошибок:

    • Добавляем обработку исключений и логирование ошибок для улучшения устойчивости функции.

Изменения в функции send_notifications

Поскольку мы изменили реализацию функции get_sol_transaction, но её сигнатура осталась прежней, изменения в send_notifications минимальны.

Обновлённая функция send_notifications:

python
async def send_notifications(bot: Bot, timeout: int): while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Не удалось получить транзакции для кошелька {wallet.wallet_hash}: {repr(e)}") continue diff = new_links - old_links for link in diff: # Передаём wallet.wallet_hash в функцию get_sol_transaction transaction = await get_sol_transaction(link, wallet.wallet_hash) if transaction is None: logging.error(f"Не удалось получить детали транзакции для {link}. Пропуск.") continue text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += transaction['amount_info'] await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

Пояснения:

  • Передача адреса кошелька:

    • Вызов get_sol_transaction остался без изменений, так как сигнатура функции такая же.
    • Убедитесь, что wallet.wallet_hash содержит корректный адрес кошелька пользователя.
  • Обработка результата:

    • Поскольку формат возвращаемого значения не изменился, дальнейшая обработка данных в send_notifications остаётся прежней.

Дополнительные рекомендации

1. Обновить функцию get_token_symbol, если необходимо:

Убедитесь, что функция get_token_symbol инициализирована и может правильно преобразовывать адреса токенов в их символы.

python
def get_token_symbol(mint_address): symbol = token_map.get(mint_address) if symbol: return symbol else: logging.warning(f"Неизвестный токен с mint адресом: {mint_address}") return mint_address # Или 'Unknown Token'

2. Загрузить список токенов (token_map):

Убедитесь, что token_map загружен и доступен для функции get_token_symbol.

3. Тестирование:

  • Проверка с различными транзакциями:

    • Протестируйте функцию get_sol_transaction с разными типами транзакций: переводы SOL, токеновые переводы, свапы и т.д.
    • Убедитесь, что функция корректно обрабатывает все случаи и возвращает ожидаемые результаты.
  • Логирование:

    • Включите отладочное логирование, чтобы отслеживать процесс парсинга и выявлять возможные проблемы.

4. Обработка специальных случаев:

  • Свапы и сложные транзакции:

    • Учтите, что в сложных транзакциях (например, свапах) могут быть дополнительные инструкции и изменения балансов.
    • Возможно, потребуется расширить логику парсинга, чтобы корректно обрабатывать такие случаи.

5. Обработка комиссий:

  • Если необходимо, добавьте обработку комиссий, которые могут влиять на баланс кошелька пользователя.

Заключение

  • Переписали функцию get_sol_transaction:

    • Теперь она использует API Solscan для получения деталей транзакции.
    • Парсит необходимые данные из ответа и возвращает информацию в том же формате.
  • Изменения в других функциях минимальны:

    • Функция send_notifications остаётся практически без изменений.
    • Убедитесь, что все функции корректно взаимодействуют друг с другом.
  • Дополнительные шаги:

    • Тщательно протестируйте обновлённый код.
    • Добавьте обработку возможных ошибок и специальных случаев.

Пример использования функции get_sol_transaction:

python
# Пример вызова функции с тестовой транзакцией и кошельком пользователя transaction = await get_sol_transaction('TxSignatureHere', 'UserWalletAddressHere') if transaction: print(transaction) else: print("Не удалось получить детали транзакции.")

Пример ожидаемого результата:

text
{ "transaction": "TxSignatureHere", "sender": "UserWalletAddressHere", "receiver": "N/A", "amount_info": "Получено: 0.000015 SOL\nОтправлено: 25.68584 WEN\n" }

Если у вас возникнут вопросы или потребуется дополнительная помощь с реализацией или отладкой кода, пожалуйста, дайте знать, и я с радостью помогу вам дальше!

Câu hỏi

INFO:sqlalchemy.engine.Engine:ROLLBACK
INFO:httpx:HTTP Request: POST https://api.mainnet-beta.solana.com "HTTP/1.1 200 OK"
ERROR:root:Не удалось получить данные транзакции 5RYzuta7rDCyAF8a4gwHbG1bSS1e9ZYp9o9HU4uHG3v4Ck8AiCnpzYz45Buf2mUn5KimNm8HpqSJSEye6FbVedrS. Код ответа: 404
ERROR:root:Не удалось получить детали транзакции для 5RYzuta7rDCyAF8a4gwHbG1bSS1e9ZYp9o9HU4uHG3v4Ck8AiCnpzYz45Buf2mUn5KimNm8HpqSJSEye6FbVedrS. Пропуск.
INFO:httpx:HTTP Request: POST https://api.mainnet-beta.solana.com "HTTP/1.1 200 OK"

Suy nghĩ

Понимание запроса

Пользователь предоставляет новую информацию об ошибке, связанной с невозможностью получения данных транзакции. Мой шаг — разобраться в запросе, чтобы предложить решение.

Рассматриваю ошибки

Окей, давайте разберемся: ошибка 404 на Solscan API. Это вызывает вопросы.

Исследую запросы

Интересно, что запросы к Solana JSON-RPC продолжаются, несмотря на использование API Solscan.

Ищу оптимальный подход

Окей, я смотрю на ошибку 404 при работе с Solscan API. Это может означать неиндексацию транзакции или неправильный хеш. Возможно, стоит добавить повторные попытки или проверку хеша.

Под рынок změnи API

Выясняю причину ошибки

Ошибка 404 указывает на проблему с Solscan API при обращении к данным транзакции. Возможно, это происходит из-за задержки в индексации или из-за того, что запрос все еще направлен к Solana RPC API.

Проверяю транзакционные хэши

Проверка корректности хэша транзакции и наличие актуальных API ключей для использования Solscan, учитывая новые политики и ограничения.

Методы индикации

Информация о транзакции приходит через RPC API Solana или Solscan, оформив обновления с использованием ключа API Solscan и обрабатывая пропуски и ошибки.

Обновляю API

Предлагаю использовать API Solscan для получения последней транзакции Solana, добавив ключ API для аутентификации.

Проверяю варианты

Сначала смотрю на документацию Solscan's API, чтобы убедиться, что конечная точка и параметры в порядке. Затем думаю о переходе на Solana RPC API из-за возможных проблем с Solscan's API.

Изучаю API Solscan

Давай посмотрим: запрашиваю последние транзакции кошелька через Solscan, обрабатываю ошибки и предполагаю резервные механизмы на Solana RPC API.

Trả lời

Здравствуйте,

Я вижу, что вы столкнулись со следующей ошибкой:

text
ERROR:root:Не удалось получить данные транзакции 5RYzuta7rDCyAF8a4gwHbG1bSS1e9ZYp9o9HU4uHG3v4Ck8AiCnpzYz45Buf2mUn5KimNm8HpqSJSEye6FbVedrS. Код ответа: 404 ERROR:root:Не удалось получить детали транзакции для 5RYzuta7rDCyAF8a4gwHbG1bSS1e9ZYp9o9HU4uHG3v4Ck8AiCnpzYz45Buf2mUn5KimNm8HpqSJSEye6FbVedrS. Пропуск.

Понимание проблемы:

  • Код ответа 404 означает, что API Solscan не смог найти данные по указанной транзакции.
  • Продолжение запросов к https://api.mainnet-beta.solana.com указывает на то, что часть вашего кода всё ещё использует Solana RPC API.

Возможные причины:

  1. Транзакция ещё не проиндексирована Solscan:

    • Если транзакция очень новая, Solscan мог её ещё не обработать.
    • Между подтверждением транзакции в сети Solana и появлением её в API Solscan может быть задержка.
  2. Неправильный хеш транзакции:

    • Хеш транзакции может быть некорректным или содержать опечатку.
  3. API Solscan требует API-ключ:

    • С августа 2022 года Solscan ввёл ограничения на использование публичного API.
    • Некоторые эндпоинты требуют наличия API-ключа.
  4. Ограничения по количеству запросов:

    • Вы могли превысить лимит запросов, что приводит к ошибкам.
  5. Изменения в API или устаревший эндпоинт:

    • Эндпоинт или способ передачи параметров могли измениться.

Решение проблемы:

1. Получение API-ключа для Solscan:

  • Перейдите на сайт Solscan и подайте заявку на получение API-ключа.
  • Следуйте инструкциям Solscan по использованию API-ключа и ознакомьтесь с лимитами запросов.

2. Обновление функции get_sol_transaction с использованием API-ключа:

python
import aiohttp import logging async def get_sol_transaction(tx_signature: str, user_wallet: str) -> dict | None: """ Получение информации о транзакции через API Solscan по ее подписи. """ url = f'https://public-api.solscan.io/transaction/{tx_signature}' headers = { 'accept': 'application/json', 'token': 'ВАШ_API_КЛЮЧ' # Замените на ваш реальный API-ключ } try: async with aiohttp.ClientSession() as session: async with session.get(url, headers=headers) as resp: if resp.status != 200: logging.error(f"Не удалось получить данные транзакции {tx_signature}. Код ответа: {resp.status}") return None response = await resp.json() if not response: logging.error(f"Пустой ответ для транзакции {tx_signature}") return None # Далее обрабатывайте response как раньше # ... except Exception as e: logging.error(f"Произошла ошибка при получении транзакции {tx_signature}: {e}") return None

3. Обновление других функций, использующих Solscan API:

Если у вас есть другие функции, обращающиеся к API Solscan (например, get_latest_sol_transactions), убедитесь, что они также используют API-ключ.

Пример обновленной функции get_latest_sol_transactions:

python
async def get_latest_sol_transactions(wallet_address: str) -> List[str]: """ Получение последних транзакций кошелька через API Solscan. """ url = f'https://public-api.solscan.io/account/transactions?address={wallet_address}&limit=10' headers = { 'accept': 'application/json', 'token': 'ВАШ_API_КЛЮЧ' # Замените на ваш реальный API-ключ } try: async with aiohttp.ClientSession() as session: async with session.get(url, headers=headers) as resp: if resp.status != 200: logging.error(f"Не удалось получить транзакции для кошелька {wallet_address}. Код ответа: {resp.status}") return [] response = await resp.json() tx_hashes = [tx.get('txHash') for tx in response] return tx_hashes except Exception as e: logging.error(f"Произошла ошибка при получении транзакций для кошелька {wallet_address}: {e}") return []

4. Обработка ошибок и задержек:

  • Проверяйте доступность транзакции: Если транзакция очень новая, возможно, она ещё не доступна через API Solscan. Подумайте о добавлении задержки или повторных попыток.
  • Обработка кода ответа 404: Добавьте специальную обработку для кода 404, возможно, с повторной попыткой через некоторое время.

5. Проверка хеша транзакции:

  • Убедитесь, что хеш транзакции корректен и существует.
  • Попробуйте открыть ссылку в браузере: https://solscan.io/tx/5RYzuta7... и проверьте, отображается ли транзакция.

6. Обновление логики парсинга:

  • Убедитесь, что вы корректно обрабатываете ответ от API Solscan.
  • Возможно, структура ответа изменилась, и вам нужно обновить логику парсинга.

7. Обновление зависимостей и документации:

  • Проверьте актуальность документации API Solscan и убедитесь, что используете правильные эндпоинты и параметры.
  • Обновите зависимости (aiohttp, asyncio и др.) до последних версий, если необходимо.

Продолжение запросов к https://api.mainnet-beta.solana.com:

Причина:

  • Ваш код всё ещё выполняет запросы к Solana RPC API для получения транзакций.
  • Например, функция get_latest_sol_transactions может использовать Solana RPC API.

Решение:

  • Если вы хотите полностью перейти на использование API Solscan, обновите все функции, которые обращаются к Solana RPC API.
  • Если вы предпочитаете оставить получение списка транзакций через Solana RPC API, убедитесь, что это соответствует вашим требованиям.

Пример использования Solana RPC API в get_latest_sol_transactions:

Если вы хотите оставить эту функцию без изменений, можете оставить запросы к https://api.mainnet-beta.solana.com.


Дополнительные рекомендации:

1. Ограничения и лимиты API Solscan:

  • Ознакомьтесь с ограничениями по количеству запросов и другими условиями использования API Solscan.
  • Возможно, вам потребуется настроить задержки между запросами или оптимизировать код для уменьшения количества обращений.

2. Логирование и отладка:

  • Добавьте больше подробного логирования, чтобы отслеживать, какие запросы выполняются и какие ответы получаются.
  • Это поможет быстрее обнаруживать и устранять проблемы.

3. Обработка других кодов ответа:

  • Помимо 404, обработайте другие возможные коды ответа (например, 429 — слишком много запросов).
  • Реализуйте механизмы повторных попыток с экспоненциальной задержкой.

4. Альтернативные решения:

  • Если использование API Solscan вызывает сложности, рассмотрите возможность использования других сервисов или продолжайте использовать Solana RPC API.
  • Например, вы можете использовать публичные RPC узлы или развернуть свой собственный узел для получения данных о транзакциях.

Заключение:

  • Получите и используйте API-ключ Solscan: Это, вероятно, решит проблему с кодом ответа 404.
  • Обновите все функции, обращающиеся к API Solscan, чтобы использовать API-ключ.
  • Проверьте корректность хеша транзакции и убедитесь, что транзакция доступна через API Solscan.
  • Добавьте обработку ошибок и логирование для лучшей диагностики проблем.

Если у вас есть дополнительные вопросы или вам нужна помощь с другими аспектами вашего кода, пожалуйста, дайте знать, и я с радостью помогу вам!

Câu hỏi

Верни все так, чтобы он использовал mainnet соланы и в случае обнаружения транзакции вне токена соланы писал об этом в уведомлении, при этом выдавая транзакции только касающиеся соланы. Например, чтобы при свапе токена, он просто писал сколько SOL ушло на свап, при этом не затрагивая получение этих же денег в другом токене. Также и наоборот, чтобы когда пользователь трейдил любой другой токен в солану, ему приходило уведомление только о пополнении в SOL, не затрагивая начальный токен, с которого он менял. Естественно, сумма в SOL должна отображаться корректно

Suy nghĩ

Реализую мониторинг

Интересно понять, как бот контролирует транзакции Solana и отправляет уведомления в Telegram. Исправляю ошибки и поддерживаю разнообразие транзакций.

Учитываю детали

Теперь я замечаю, что необходимо снизить влияние на начальный токен и анализировать только суммы на Solana, избегая упоминания внешних транзакций.

Понимание предпочтений

Пользователь хочет, чтобы бот показывал только транзакции с SOL, игнорируя информацию о другом токене.

Уточняю детали
Я разбираюсь с задачей информировать о передаче SOL в процессе токен-свапов, исключая лишние токены. Это подразумевает возвращение к API Solana mainnet и корректировку функций для точного вывода данных.

Перевод кода

Я предлагаю вернуться к использованию API Solana mainnet для трансакций с SOL, исключая токенов, и корректное отображение суммы SOL.

Trả lời

Здравствуйте,

Понимаю, что вы хотите вернуть код к использованию Solana Mainnet API и настроить его так, чтобы бот отправлял уведомления только о транзакциях с SOL. При этом, когда происходит свап токена, бот должен сообщать только о том, сколько SOL было отправлено или получено, не затрагивая другие токены.


План действий:

  1. Вернуть использование Solana Mainnet API: Переписать функцию get_sol_transaction для использования getTransaction метода Solana RPC API.

  2. Обновить логику парсинга: Изменить функцию так, чтобы она учитывала только транзакции, связанные с SOL.

  3. Обрабатывать только SOL-транзакции: В случае свапа токенов, бот будет сообщать только о том, сколько SOL было потрачено или получено.

  4. Обновить другие функции, если необходимо: Убедиться, что send_notifications и другие функции корректно работают с обновлённой логикой.


1. Вернуть использование Solana Mainnet API

Мы будем использовать метод getTransaction из Solana RPC API.

Функция get_sol_transaction:

python
import aiohttp import logging async def get_sol_transaction(tx_signature: str, user_wallet: str) -> dict | None: """ Получение информации о транзакции Solana по ее подписи. """ url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [ tx_signature, { "encoding": "jsonParsed", "maxSupportedTransactionVersion": 0, "commitment": "confirmed" } ] } try: async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json() if 'result' not in response or response['result'] is None: logging.error(f"Транзакция {tx_signature} не найдена или произошла ошибка.") return None result = response['result'] transaction = result['transaction'] message = transaction['message'] account_keys = message['accountKeys'] account_pubkeys = [account['pubkey'] for account in account_keys] meta = result.get('meta', {}) pre_balances = meta.get('preBalances', []) post_balances = meta.get('postBalances', []) # Ищем изменения баланса SOL для кошелька пользователя sol_change = 0 for idx, (pre_balance, post_balance) in enumerate(zip(pre_balances, post_balances)): pubkey = account_pubkeys[idx] if pubkey != user_wallet: continue amount_change = (post_balance - pre_balance) / 1e9 # Преобразуем лампорты в SOL sol_change += amount_change if sol_change == 0: logging.info(f"Нет изменений баланса SOL для транзакции {tx_signature} и кошелька {user_wallet}") return None # Определяем действие (получено или отправлено) action = 'Получено' if sol_change > 0 else 'Отправлено' amount_formatted = "{:.9f}".format(abs(sol_change)).rstrip('0').rstrip('.') sender = user_wallet receiver = 'N/A' amount_info = f"{action}: {amount_formatted} SOL\n" return { "transaction": tx_signature, "sender": sender, "receiver": receiver, "amount_info": amount_info, } except Exception as e: logging.error(f"Произошла ошибка при получении транзакции {tx_signature}: {e}") return None

Пояснения:

  • Использование Solana Mainnet API:

    • Выполняем POST-запрос к https://api.mainnet-beta.solana.com с методом getTransaction.
    • Используем encoding: jsonParsed для удобного парсинга.
  • Парсинг SOL-транзакций:

    • Извлекаем preBalances и postBalances, которые содержат балансы в лампортах (1 SOL = 1e9 лампортов).
    • Сопоставляем балансы до и после транзакции по индексам.
    • Фильтруем по адресу кошелька пользователя user_wallet.
    • Вычисляем изменение баланса SOL для пользователя.
  • Игнорирование токеновых транзакций:

    • Не обрабатываем preTokenBalances и postTokenBalances.
    • Не обращаем внимания на инструкции, связанные с токенами.
  • Формирование уведомления:

    • Если изменение баланса SOL не равно нулю, формируем сообщение с информацией об отправленных или полученных SOL.
    • Отображаем только сумму в SOL.

2. Обновление функции send_notifications

Функция send_notifications остаётся практически без изменений, но убедитесь, что она корректно передаёт wallet.wallet_hash в функцию get_sol_transaction.

python
async def send_notifications(bot: Bot, timeout: int): while True: wallets = await PostgresRequests().get_all_wallets() for wallet in wallets: old_links = await get_links(wallet_hash=wallet.wallet_hash) try: new_links = set(await get_latest_sol_transactions(wallet.wallet_hash)) except Exception as e: logging.error(f"Не удалось получить транзакции для кошелька {wallet.wallet_hash}: {repr(e)}") continue diff = new_links - old_links for link in diff: # Передаём wallet.wallet_hash в функцию get_sol_transaction transaction = await get_sol_transaction(link, wallet.wallet_hash) if transaction is None: logging.info(f"Нет изменений SOL для транзакции {link}. Пропуск.") continue text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += transaction['amount_info'] await bot.send_message(chat_id=wallet.tg_id, text=text) await store_links(wallet_hash=wallet.wallet_hash, links=new_links) await asyncio.sleep(timeout)

Пояснения:

  • Логирование:

    • Изменили уровень логирования для случаев, когда нет изменений SOL, с ERROR на INFO, так как это не является ошибкой.
  • Уведомления:

    • Бот будет отправлять уведомления только о транзакциях, где есть изменения баланса SOL для пользователя.

3. Обновление функции get_latest_sol_transactions

Функция get_latest_sol_transactions может остаться без изменений, если она корректно возвращает последние транзакции пользователя.

Пример:

python
async def get_latest_sol_transactions(wallet_address: str) -> List[str]: url = 'https://api.mainnet-beta.solana.com' headers = {'Content-Type': 'application/json'} payload = { "jsonrpc": "2.0", "id": 1, "method": "getSignaturesForAddress", "params": [ wallet_address, { "limit": 10, "commitment": "confirmed" } ] } try: async with aiohttp.ClientSession() as session: async with session.post(url, json=payload, headers=headers) as resp: response = await resp.json() if 'result' not in response or response['result'] is None: logging.error(f"Не удалось получить транзакции для кошелька {wallet_address}.") return [] signatures = [entry['signature'] for entry in response['result']] return signatures except Exception as e: logging.error(f"Произошла ошибка при получении транзакций для кошелька {wallet_address}: {e}") return []

4. Тестирование и проверка работы

  • Свап SOL на токен:

    • Когда пользователь обменивает SOL на токен, происходит списание SOL.
    • Бот должен отправить уведомление с суммой SOL, которая была отправлена.
  • Свап токена на SOL:

    • Когда пользователь обменивает токен на SOL, происходит зачисление SOL.
    • Бот должен отправить уведомление с суммой SOL, которая была получена.
  • Пример сообщения от бота:

    text
    Транзакция: Drtwa7aDfyDs5jsU6dwx1CEoJzFDJNnBB3YjcvQR7Te6aRJ3YYXMi3gBB5EFuyNpHaA3xs8Mbgabp8f912Ytqff Отправитель: ВашКошелек Получатель: N/A Отправлено: 0.000015 SOL

5. Дополнительные рекомендации

1. Обработка комиссий:

  • При транзакциях SOL баланс может уменьшаться из-за комиссий.
  • Если вы хотите игнорировать комиссии и учитывать только реальные отправления и получения SOL, потребуется более сложная логика.

2. Улучшение определения получателя:

  • В некоторых транзакциях может быть известно, кто является получателем.
  • Вы можете попытаться определить получателя, анализируя инструкции транзакции.

3. Логирование:

  • Добавьте подробное логирование для отладки и мониторинга работы бота.
  • Например, логируйте транзакции, которые были пропущены, и причины пропуска.

Заключение

  • Использование Solana Mainnet API:

    • Вернули использование Solana RPC API для получения деталей транзакций.
    • Парсим только изменения баланса SOL.
  • Уведомления только о SOL:

    • Бот отправляет уведомления только о транзакциях, где есть изменения баланса SOL для пользователя.
    • При свапах бот сообщает только о потраченном или полученном SOL, игнорируя другие токены.
  • Минимальные изменения в других функциях:

    • Обновили функцию send_notifications для передачи адреса кошелька пользователя.
    • Остальные функции остаются без значительных изменений.
  • Тестирование:

    • Рекомендуется протестировать бота с различными типами транзакций, чтобы убедиться, что он работает корректно.

Если вы столкнётесь с проблемами или у вас будут дополнительные вопросы:

  • Проблемы с отображением сумм:

    • Если суммы SOL отображаются некорректно, проверьте логику вычисления sol_change в функции get_sol_transaction.
  • Отсутствие уведомлений:

    • Убедитесь, что функция get_latest_sol_transactions корректно получает последние транзакции.
    • Проверьте, что бот правильно сравнивает новые транзакции с сохранёнными.
  • Ошибки в логах:

    • Внимательно изучите логи приложения на предмет ошибок и исключений.
    • Добавьте больше логирования для отладки.

Если вам потребуется дополнительная помощь с настройкой или отладкой кода, пожалуйста, дайте мне знать, и я с радостью помогу вам!

Câu hỏi

Что делать с функциями load_token_list и get_token_symbol?

Suy nghĩ

Изучаю детали

Окей, я разбираюсь в вопросах пользователя и приоритетах. Оцениваю возможность выполнения заявленных задач с учетом нововведений в закон.

Уточняю задачи модели.

Уточняю функции

Я обновляю код, чтобы фокусироваться только на транзакциях с SOL. Теперь потребуется отдельно разобрать функции load_token_list и get_token_symbol.

Понимание необходимости функций

Похоже, что функциям, работающим с токенами, больше нет нужды. Новая логика касается только SOL-транзакций. Интересно, как это влияет на текущую структуру.

Обновление и удаление

В связи с тем, что теперь обрабатываются только транзакции SOL, функции load_token_list и get_token_symbol теряют актуальность и могут быть удалены.

Trả lời

Здравствуйте,

Поскольку вы решили изменить логику бота так, чтобы он обрабатывал только транзакции, связанные с SOL, и игнорировал все остальные токены, функции load_token_list и get_token_symbol больше не нужны в вашем коде. Эти функции изначально предназначались для отображения символов токенов при обработке транзакций с различными токенами.


Что делать с функциями load_token_list и get_token_symbol

1. Удалить ненужные функции и связанные с ними части кода

  • Удалите функции:

    • load_token_list
    • get_token_symbol
  • Удалите загрузку списка токенов:

    • Если в вашем коде есть вызов load_token_list() при запуске приложения, его тоже можно удалить.
  • Удалите переменные:

    • Если у вас есть глобальные переменные, связанные со списком токенов, например token_map, их можно удалить.

2. Проверить оставшийся код на наличие ссылок на эти функции

  • В функции get_sol_transaction:

    • Убедитесь, что в обновлённой функции нет ссылок на get_token_symbol или использование переменной token.

    • В текущей реализации вы обрабатываете только изменения баланса SOL, поэтому переменная token не используется.

  • В функции send_notifications:

    • Проверьте, что формирование сообщения не зависит от переменной token.

    • Ваше сообщение должно отображать только информацию о SOL.

3. Обновить формирование сообщения

  • Обновите amount_info в функции get_sol_transaction:

    • Убедитесь, что amount_info формируется корректно и не использует переменную token.
    python
    amount_info = f"{action}: {amount_formatted} SOL\n"
  • Обновите формирование текста сообщения в send_notifications:

    • Убедитесь, что текст сообщения не использует переменную token или другие связанные с токенами данные.

Проверка и тестирование

1. Тестируйте бота с различными транзакциями

  • Отправка SOL на другой адрес:

    • Бот должен отправить уведомление о списании SOL.
  • Получение SOL от другого адреса:

    • Бот должен отправить уведомление о получении SOL.
  • Свап SOL на токен:

    • Бот должен отправить уведомление о списании SOL.
  • Свап токена на SOL:

    • Бот должен отправить уведомление о получении SOL.

2. Убедитесь, что бот игнорирует транзакции без изменений SOL

  • Если транзакция не приводит к изменению баланса SOL для пользователя, бот не должен отправлять уведомление.

Общий итог

  • Удаление функций: Поскольку вы больше не обрабатываете транзакции с токенами, функции load_token_list и get_token_symbol больше не нужны и могут быть удалены.

  • Упрощение кода: Удаление этих функций упрощает код и снижает вероятность возникновения ошибок, связанных с обработкой токенов.

  • Фокус на SOL-транзакциях: Бот теперь будет более точным и эффективным при обработке только SOL-транзакций, соответствуя вашим требованиям.


Дополнительные рекомендации

1. Обновите документацию и комментарии

  • Обновите комментарии в коде, чтобы они отражали текущую логику и не ссылались на удалённые функции.

  • Если у вас есть документация или README-файл, убедитесь, что они актуальны.

2. Очистите импортированные модули

  • Если у вас есть импортированные модули, которые использовались только в load_token_list и get_token_symbol (например, requests для загрузки списка токенов), их можно удалить.

    python
    # Пример удаляемого импорта import requests

3. Улучшите логирование

  • Поскольку ваш код стал проще, вы можете улучшить логирование для SOL-транзакций.

  • Добавьте логирование успешных транзакций и потенциальных ошибок.

4. Подготовьтесь к будущим изменениям

  • Если в будущем вы решите снова обрабатывать токеновые транзакции, сохраните резервную копию функций load_token_list и get_token_symbol для возможного использования.

Пример обновлённого кода

Функция get_sol_transaction:

python
async def get_sol_transaction(tx_signature: str, user_wallet: str) -> dict | None: """ Получение информации о транзакции Solana по ее подписи. """ # ... [код функции без использования load_token_list и get_token_symbol] ...

Функция send_notifications:

python
async def send_notifications(bot: Bot, timeout: int): # ... [код функции без изменений, но убедитесь, что нет ссылок на токены] ...

Заключение

Удаление функций load_token_list и get_token_symbol соответствует вашей текущей задаче: бот должен обрабатывать только SOL-транзакции и игнорировать другие токены. Это упрощает код и делает его более понятным и поддерживаемым.


Если у вас возникнут дополнительные вопросы или потребуется помощь с другими аспектами вашего кода, пожалуйста, дайте мне знать, и я с радостью помогу вам!

Câu hỏi

Что делать с функциями load_token_list и get_token_symbol?

Suy nghĩ

Пересматриваю наименования

Рассматриваю функции load_token_list и get_token_symbol для транзакций SOL. Сейчас они не нужны, так как бот обрабатывает только транзакции с SOL и игнорирует другие токены.

Обсуждая задачи

Пользователь повторяет свои вопросы о функциях load_token_list и get_token_symbol, возможно, хочет большего разъяснения или дополнительной информации.

Удаляю ненужные функции

Похоже, что функции load_token_list и get_token_symbol больше не нужны с учетом того, что бот обрабатывает только транзакции SOL.

Ищем применение

Ассистент размышляет над функциями load_token_list и get_token_symbol, предлагая их удалить, так как они больше не нужны для обработки транзакций SOL, подобно предыдущему примеру.

Обновляю и упрощаю

Я убеждаюсь, что код, зависящий от удалённых функций, обновляется для обработки только транзакций SOL, что упрощает код и снижает вероятность ошибок.

Trả lời

Здравствуйте,

Поскольку ваш бот теперь настроен на обработку только транзакций, связанных с SOL, и игнорирует все другие токены, функции load_token_list и get_token_symbol больше не нужны в вашем коде. Эти функции использовались для загрузки списка токенов и преобразования адресов mint токенов в их символы при обработке транзакций с разными токенами.

Вот что вам следует сделать с этими функциями:


1. Удалите функции load_token_list и get_token_symbol

  • Удалите определения функций:

    Удалите полный код функций load_token_list и get_token_symbol из вашего проекта.

    python
    # Удалите этот код def load_token_list(): # Код функции... def get_token_symbol(mint_address): # Код функции...

2. Удалите вызовы и использование этих функций

  • Удалите вызовы функций в вашем коде:

    Проверьте ваш код на наличие вызовов этих функций и удалите их.

    python
    # Пример удаления вызова функции # Удалите или закомментируйте эту строку load_token_list()
  • Удалите использование get_token_symbol:

    В функции get_sol_transaction или в других местах убедитесь, что вы не вызываете get_token_symbol.

    python
    # Пример удаления использования функции # Удалите или замените этот код token = get_token_symbol(token_mint)

3. Удалите связанные переменные и импорты

  • Удалите переменные, связанные со списком токенов:

    Если у вас есть глобальные переменные, связанные с функциями, например token_map, удалите их.

    python
    # Удалите эту переменную token_map = {}
  • Удалите ненужные импорты:

    Если функции использовали модули, которые больше не нужны (например, requests), удалите их из импортов.

    python
    # Удалите ненужные импорты import requests

4. Обновите функции, чтобы они не зависели от удалённых функций

  • Обновите функцию get_sol_transaction:

    Убедитесь, что в этой функции нет ссылок на удалённые функции или переменные. Например, если вы ранее использовали token или token_mint, удалите или замените их.

    python
    # Убедитесь, что код не использует переменные token или token_mint amount_info = f"{action}: {amount_formatted} SOL\n"
  • Обновите функцию send_notifications:

    Проверьте, что формирование сообщений не использует переменные или данные, связанные с токенами.

    python
    # Пример формирования сообщения text = f"Транзакция: {transaction['transaction']} \n" text += f"Отправитель: {transaction['sender']} \n" text += f"Получатель: {transaction['receiver']} \n" text += transaction['amount_info']

5. Обновите комментарии и документацию

  • Проверьте и обновите комментарии:

    Убедитесь, что комментарии в коде соответствуют текущей функциональности и не ссылаются на удалённые функции.

  • Обновите документацию:

    Если у вас есть документация или инструкции по использованию бота, обновите их, чтобы отразить изменения.

6. Тестируйте обновлённый код

  • Проведите тестирование:

    Убедитесь, что бот работает корректно после удаления функций. Проверьте обработку транзакций с SOL и убедитесь, что уведомления отправляются правильно.

  • Проверка на ошибки:

    Запустите код и просмотрите логи на предмет ошибок или исключений, связанных с удалёнными функциями.


Преимущества удаления этих функций:

  • Упрощение кода: Код станет более чистым и понятным, что облегчает его поддержку и развитие.

  • Снижение вероятности ошибок: Удаление неиспользуемых функций и переменных уменьшает риск возникновения ошибок, связанных с ними.

  • Оптимизация производительности: Бот будет работать немного быстрее, так как не будет загружать и обрабатывать ненужные данные о токенах.


Пример обновлённого кода функции get_sol_transaction:

python
async def get_sol_transaction(tx_signature: str, user_wallet: str) -> dict | None: """ Получение информации о транзакции Solana по ее подписи. """ # Ваш обновлённый код, который обрабатывает только SOL-транзакции # Убедитесь, что нет ссылок на token или get_token_symbol

Пример обновлённого кода функции send_notifications:

python
async def send_notifications(bot: Bot, timeout: int): while True: # Ваш код для отправки уведомлений # Убедитесь, что сообщения формируются правильно и не используют данные о токенах

Если в будущем вы решите снова обрабатывать транзакции с другими токенами, вы можете сохранить резервную копию удалённых функций или вернуться к предыдущей версии кода.

Если у вас возникнут трудности или дополнительные вопросы при удалении функций или обновлении кода, пожалуйста, дайте мне знать, и я с радостью помогу вам!

Chia sẻ Q&A này