mirror of
https://github.com/bohd4nx/FragmentAPI.git
synced 2026-07-25 06:14:29 +00:00
feat: implement logging throughout the application and enhance error handling
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from ton_core import NetworkGlobalID
|
||||
@@ -16,6 +17,9 @@ if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def get_usdt_balance(ton: Any, wallet_address: str) -> float:
|
||||
"""Return the USDT balance for a Fragment-linked TON wallet."""
|
||||
try:
|
||||
@@ -29,9 +33,12 @@ async def get_usdt_balance(ton: Any, wallet_address: str) -> float:
|
||||
return float(raw_balance) / 1_000_000.0
|
||||
except ProviderResponseError as exc:
|
||||
if exc.code == 404:
|
||||
logger.debug("No USDT jetton wallet found for '%s'; treating balance as 0", wallet_address)
|
||||
return 0.0
|
||||
logger.error("Failed to load USDT balance for wallet '%s': %s", wallet_address, exc, exc_info=True)
|
||||
raise WalletError(WalletError.USDT_BALANCE_CHECK_FAILED.format(exc=exc)) from exc
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to load USDT balance for wallet '%s' due to an unexpected error", wallet_address)
|
||||
raise WalletError(WalletError.USDT_BALANCE_CHECK_FAILED.format(exc=exc)) from exc
|
||||
|
||||
|
||||
@@ -47,6 +54,11 @@ async def check_ton_payment_balance(
|
||||
|
||||
required_ton = max(tx_price_ton, MIN_TON_BALANCE)
|
||||
if balance_ton < required_ton:
|
||||
logger.error(
|
||||
"Failed TON balance check: balance=%s TON, required=%s TON",
|
||||
round(balance_ton, 6),
|
||||
round(required_ton, 6),
|
||||
)
|
||||
raise WalletError(WalletError.LOW_TON_BALANCE.format(balance=balance_ton, required=required_ton))
|
||||
|
||||
|
||||
@@ -58,11 +70,22 @@ async def check_usdt_payment_balance(
|
||||
) -> None:
|
||||
"""Validate that the wallet can cover a USDT-denominated payment."""
|
||||
if balance_ton < MIN_TON_BALANCE:
|
||||
logger.error(
|
||||
"Failed TON gas reserve check for USDT payment: balance=%s TON, required=%s TON",
|
||||
round(balance_ton, 6),
|
||||
MIN_TON_BALANCE,
|
||||
)
|
||||
raise WalletError(WalletError.LOW_TON_BALANCE.format(balance=balance_ton, required=MIN_TON_BALANCE))
|
||||
|
||||
usdt_balance = await get_usdt_balance(ton, wallet_address)
|
||||
required_usdt = required_payment_amount if required_payment_amount is not None else MIN_USDT_BALANCE
|
||||
if usdt_balance < required_usdt:
|
||||
logger.error(
|
||||
"Failed USDT balance check for wallet '%s': balance=%s USDT, required=%s USDT",
|
||||
wallet_address,
|
||||
round(usdt_balance, 6),
|
||||
round(required_usdt, 6),
|
||||
)
|
||||
raise WalletError(WalletError.LOW_USDT_BALANCE.format(balance=usdt_balance, required=required_usdt))
|
||||
|
||||
|
||||
@@ -80,6 +103,7 @@ async def get_account_info(client: FragmentClient) -> dict[str, Any]:
|
||||
"walletStateInit": base64.b64encode(boc).decode(),
|
||||
}
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to build Fragment account info from the configured wallet")
|
||||
raise WalletError(WalletError.ACCOUNT_INFO_FAILED.format(exc=exc)) from exc
|
||||
|
||||
|
||||
@@ -99,4 +123,5 @@ async def get_wallet_info(client: FragmentClient) -> WalletInfo:
|
||||
usdt_balance=round(usdt_balance, 4),
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to fetch wallet info from Tonapi")
|
||||
raise WalletError(WalletError.WALLET_INFO_FAILED.format(exc=exc)) from exc
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import logging
|
||||
import random
|
||||
import ssl
|
||||
from typing import TYPE_CHECKING, Any
|
||||
@@ -19,6 +20,9 @@ if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def clean_decode(payload: str) -> str | Cell:
|
||||
"""Decode a base64 BOC comment from Fragment into text when possible.
|
||||
|
||||
@@ -43,6 +47,7 @@ def clean_decode(payload: str) -> str | Cell:
|
||||
except UnicodeDecodeError:
|
||||
return cell
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to decode Fragment payload")
|
||||
raise ParseError(ParseError.UNPARSEABLE.format(context="payload decode", exc=exc)) from exc
|
||||
|
||||
|
||||
@@ -64,6 +69,7 @@ async def process_transaction(
|
||||
Normalized transaction hash string.
|
||||
"""
|
||||
if "transaction" not in transaction_data or not transaction_data["transaction"].get("messages"):
|
||||
logger.error("Failed to process transaction: missing transaction payload or messages")
|
||||
raise TransactionError(TransactionError.INVALID_PAYLOAD)
|
||||
|
||||
message = transaction_data["transaction"]["messages"][0]
|
||||
@@ -87,6 +93,7 @@ async def process_transaction(
|
||||
except WalletError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
logger.exception("Failed to validate balances before broadcasting transaction")
|
||||
raise WalletError(WalletError.TON_BALANCE_CHECK_FAILED.format(exc=exc)) from exc
|
||||
|
||||
try:
|
||||
@@ -103,12 +110,24 @@ async def process_transaction(
|
||||
return str(result.normalized_hash)
|
||||
except ProviderResponseError as exc:
|
||||
if exc.code == 429 and attempt == 0:
|
||||
logger.warning(
|
||||
"Broadcast rate-limited (429), retrying transaction once: %s",
|
||||
exc,
|
||||
exc_info=True,
|
||||
)
|
||||
await asyncio.sleep(1 + random.uniform(0, 0.5))
|
||||
continue
|
||||
if exc.code == 406 and "seqno" in str(exc).lower():
|
||||
if attempt < 2:
|
||||
logger.warning(
|
||||
"Broadcast seqno conflict (406), retrying attempt %s: %s",
|
||||
attempt + 2,
|
||||
exc,
|
||||
exc_info=True,
|
||||
)
|
||||
await asyncio.sleep(2 + random.uniform(0, 1))
|
||||
continue
|
||||
logger.error("Failed to broadcast transaction after seqno retries")
|
||||
raise TransactionError(TransactionError.DUPLICATE_SEQNO) from exc
|
||||
raise
|
||||
except (WalletError, TransactionError):
|
||||
@@ -117,8 +136,16 @@ async def process_transaction(
|
||||
cause: BaseException | None = exc
|
||||
while cause is not None:
|
||||
if isinstance(cause, ssl.SSLError):
|
||||
logger.exception("Failed to broadcast transaction due to SSL error")
|
||||
raise TransactionError(TransactionError.BROADCAST_FAILED_SSL.format(exc=exc)) from exc
|
||||
cause = cause.__cause__ or cause.__context__
|
||||
logger.exception(
|
||||
"Failed to broadcast transaction to '%s' for %s nanotons using payment method '%s'",
|
||||
message["address"],
|
||||
message["amount"],
|
||||
payment_method,
|
||||
)
|
||||
raise TransactionError(TransactionError.BROADCAST_FAILED.format(exc=exc)) from exc
|
||||
|
||||
logger.error("Failed to broadcast transaction: transfer loop exited without result")
|
||||
raise TransactionError(TransactionError.BROADCAST_FAILED.format(exc="transfer loop exited without result"))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ton_core import Address, NetworkGlobalID
|
||||
@@ -14,6 +15,9 @@ if TYPE_CHECKING:
|
||||
from pyfragment.client import FragmentClient
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def send_ton_transfer(
|
||||
client: FragmentClient,
|
||||
destination: str,
|
||||
@@ -47,6 +51,11 @@ async def send_ton_transfer(
|
||||
except (TransactionError, WalletError):
|
||||
raise
|
||||
except Exception as exc:
|
||||
logger.exception(
|
||||
"Failed to send TON transfer to '%s' for %s nanotons",
|
||||
destination,
|
||||
amount,
|
||||
)
|
||||
raise TransactionError(TransactionError.BROADCAST_FAILED.format(exc=exc)) from exc
|
||||
|
||||
|
||||
@@ -88,4 +97,9 @@ async def send_usdt_transfer(
|
||||
except (TransactionError, WalletError):
|
||||
raise
|
||||
except Exception as exc:
|
||||
logger.exception(
|
||||
"Failed to send USDT transfer to '%s' for %s base units",
|
||||
destination,
|
||||
usdt_amount,
|
||||
)
|
||||
raise TransactionError(TransactionError.BROADCAST_FAILED.format(exc=exc)) from exc
|
||||
|
||||
Reference in New Issue
Block a user