Coverage for app / services / blockchain.py: 35%
106 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 06:09 -0500
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 06:09 -0500
1"""Blockchain API service for querying balances and transactions."""
2import httpx
3from typing import Optional, List, Dict, Any
4from app.core.config import settings
7class BlockchainAPIError(Exception):
8 """Raised when blockchain API calls fail."""
9 pass
12class BlockchainService:
13 """Service for interacting with blockchain APIs (BlockCypher, Helius)."""
15 def __init__(self):
16 self.blockcypher_base = "https://api.blockcypher.com/v1"
17 self.helius_base = "https://api.helius.xyz/v0"
18 self.blockcypher_token = settings.blockcypher_api_token
19 self.helius_key = settings.helius_api_key
21 def _get_blockcypher_url(self, chain: str, endpoint: str) -> str:
22 """Build BlockCypher API URL."""
23 url = f"{self.blockcypher_base}/{chain}/main/{endpoint}"
24 if self.blockcypher_token:
25 url += f"?token={self.blockcypher_token}"
26 return url
28 async def get_btc_balance(self, address: str) -> int:
29 """Get BTC balance in satoshi."""
30 try:
31 url = self._get_blockcypher_url("btc", f"addrs/{address}/balance")
32 async with httpx.AsyncClient(timeout=10.0) as client:
33 response = await client.get(url)
34 response.raise_for_status()
35 data = response.json()
36 return data.get("balance", 0)
37 except httpx.HTTPError as e:
38 raise BlockchainAPIError(f"Failed to fetch BTC balance: {e}")
40 async def get_eth_balance(self, address: str) -> int:
41 """Get ETH balance in wei."""
42 try:
43 url = self._get_blockcypher_url("eth", f"addrs/{address}/balance")
44 async with httpx.AsyncClient(timeout=10.0) as client:
45 response = await client.get(url)
46 response.raise_for_status()
47 data = response.json()
48 return data.get("balance", 0)
49 except httpx.HTTPError as e:
50 raise BlockchainAPIError(f"Failed to fetch ETH balance: {e}")
52 async def get_doge_balance(self, address: str) -> int:
53 """Get DOGE balance in satoshi (smallest unit)."""
54 try:
55 url = self._get_blockcypher_url("doge", f"addrs/{address}/balance")
56 async with httpx.AsyncClient(timeout=10.0) as client:
57 response = await client.get(url)
58 response.raise_for_status()
59 data = response.json()
60 return data.get("balance", 0)
61 except httpx.HTTPError as e:
62 raise BlockchainAPIError(f"Failed to fetch DOGE balance: {e}")
64 async def get_sol_balance(self, address: str) -> int:
65 """Get SOL balance in lamports."""
66 if not self.helius_key:
67 raise BlockchainAPIError("Helius API key not configured")
69 try:
70 url = f"{self.helius_base}/addresses/{address}/balances"
71 params = {"api-key": self.helius_key}
72 async with httpx.AsyncClient(timeout=10.0) as client:
73 response = await client.get(url, params=params)
74 response.raise_for_status()
75 data = response.json()
76 # Helius returns nativeBalance in lamports
77 native_balance = data.get("nativeBalance", 0)
78 return int(native_balance)
79 except httpx.HTTPError as e:
80 raise BlockchainAPIError(f"Failed to fetch SOL balance: {e}")
82 async def get_btc_transactions(self, address: str, limit: int = 50) -> List[Dict[str, Any]]:
83 """Get BTC transactions for an address."""
84 try:
85 url = self._get_blockcypher_url("btc", f"addrs/{address}/txs")
86 params = {"limit": limit}
87 async with httpx.AsyncClient(timeout=10.0) as client:
88 response = await client.get(url, params=params)
89 response.raise_for_status()
90 data = response.json()
91 return data.get("txs", [])
92 except httpx.HTTPError as e:
93 raise BlockchainAPIError(f"Failed to fetch BTC transactions: {e}")
95 async def get_eth_transactions(self, address: str, limit: int = 50) -> List[Dict[str, Any]]:
96 """Get ETH transactions for an address."""
97 try:
98 url = self._get_blockcypher_url("eth", f"addrs/{address}/txs")
99 params = {"limit": limit}
100 async with httpx.AsyncClient(timeout=10.0) as client:
101 response = await client.get(url, params=params)
102 response.raise_for_status()
103 data = response.json()
104 return data.get("txs", [])
105 except httpx.HTTPError as e:
106 raise BlockchainAPIError(f"Failed to fetch ETH transactions: {e}")
108 async def get_doge_transactions(self, address: str, limit: int = 50) -> List[Dict[str, Any]]:
109 """Get DOGE transactions for an address."""
110 try:
111 url = self._get_blockcypher_url("doge", f"addrs/{address}/txs")
112 params = {"limit": limit}
113 async with httpx.AsyncClient(timeout=10.0) as client:
114 response = await client.get(url, params=params)
115 response.raise_for_status()
116 data = response.json()
117 return data.get("txs", [])
118 except httpx.HTTPError as e:
119 raise BlockchainAPIError(f"Failed to fetch DOGE transactions: {e}")
121 async def get_sol_transactions(self, address: str, limit: int = 50) -> List[Dict[str, Any]]:
122 """Get SOL transactions for an address."""
123 if not self.helius_key:
124 raise BlockchainAPIError("Helius API key not configured")
126 try:
127 url = f"{self.helius_base}/addresses/{address}/transactions"
128 params = {
129 "api-key": self.helius_key,
130 "limit": limit,
131 }
132 async with httpx.AsyncClient(timeout=10.0) as client:
133 response = await client.get(url, params=params)
134 response.raise_for_status()
135 data = response.json()
136 return data if isinstance(data, list) else []
137 except httpx.HTTPError as e:
138 raise BlockchainAPIError(f"Failed to fetch SOL transactions: {e}")