Skip to content

Commit 4a6d34d

Browse files
committed
feat: add Bitaps and LitecoinSpace APIs for Litecoin balance fetching and parsing, with comprehensive test coverage
1 parent c9940e1 commit 4a6d34d

7 files changed

Lines changed: 238 additions & 0 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"data": {
3+
"balance": 75763,
4+
"receivedAmount": 1137420376400653,
5+
"receivedTxCount": 629,
6+
"sentAmount": 1137420376324890,
7+
"sentTxCount": 20,
8+
"firstReceivedTxPointer": "1512464:19",
9+
"firstSentTxPointer": "1512466:8",
10+
"lastTxPointer": "3038651:188",
11+
"largestSpentTxAmount": 150393732845214,
12+
"largestSpentTxPointer": "2784397:708",
13+
"largestReceivedTxAmount": 65763,
14+
"largestReceivedTxPointer": "3038651:188",
15+
"receivedOutsCount": 629,
16+
"spentOutsCount": 627,
17+
"pendingReceivedAmount": 0,
18+
"pendingSentAmount": 0,
19+
"pendingReceivedTxCount": 0,
20+
"pendingSentTxCount": 0,
21+
"pendingReceivedOutsCount": 0,
22+
"pendingSpentOutsCount": 0,
23+
"type": "P2SH"
24+
},
25+
"time": 0.0047
26+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"address": "M8T1B2Z97gVdvmfkQcAtYbEepune1tzGua",
3+
"chain_stats": {
4+
"funded_txo_count": 629,
5+
"funded_txo_sum": 1137420376400653,
6+
"spent_txo_count": 627,
7+
"spent_txo_sum": 1137420376324890,
8+
"tx_count": 649
9+
},
10+
"mempool_stats": {
11+
"funded_txo_count": 0,
12+
"funded_txo_sum": 0,
13+
"spent_txo_count": 0,
14+
"spent_txo_sum": 0,
15+
"tx_count": 0
16+
}
17+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from decimal import Decimal
2+
3+
import pytest
4+
5+
from blockapi.test.v2.api.conftest import read_file
6+
from blockapi.test.v2.test_data import ltc_test_address
7+
from blockapi.v2.api.bitaps import BitapsLitecoinApi
8+
from blockapi.v2.models import FetchResult
9+
10+
11+
def test_fetch_balances(requests_mock, bitaps_ltc_balance_response):
12+
requests_mock.get(
13+
f'https://api.bitaps.com/ltc/v1/blockchain/address/state/{ltc_test_address}',
14+
text=bitaps_ltc_balance_response,
15+
)
16+
17+
api = BitapsLitecoinApi()
18+
balances = api.get_balance(ltc_test_address)
19+
assert len(balances) == 1
20+
assert balances[0].balance == Decimal('0.00075763')
21+
22+
23+
def test_parse_zero_balance():
24+
api = BitapsLitecoinApi()
25+
fetch_result = FetchResult(data={'data': {'balance': 0}})
26+
result = api.parse_balances(fetch_result)
27+
assert result.data is None
28+
29+
30+
def test_parse_empty_response():
31+
api = BitapsLitecoinApi()
32+
fetch_result = FetchResult(data=None)
33+
result = api.parse_balances(fetch_result)
34+
assert result.data is None
35+
36+
37+
@pytest.fixture
38+
def bitaps_ltc_balance_response():
39+
return read_file('data/bitaps_ltc_balance_response.json')
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from decimal import Decimal
2+
3+
import pytest
4+
5+
from blockapi.test.v2.api.conftest import read_file
6+
from blockapi.test.v2.test_data import ltc_test_address
7+
from blockapi.v2.api.litecoinspace import LitecoinSpaceApi
8+
from blockapi.v2.models import FetchResult
9+
10+
11+
def test_fetch_balances(requests_mock, litecoinspace_balance_response):
12+
requests_mock.get(
13+
f'https://litecoinspace.org/api/address/{ltc_test_address}',
14+
text=litecoinspace_balance_response,
15+
)
16+
17+
api = LitecoinSpaceApi()
18+
balances = api.get_balance(ltc_test_address)
19+
assert len(balances) == 1
20+
assert balances[0].balance == Decimal('0.00075763')
21+
22+
23+
def test_parse_zero_balance():
24+
api = LitecoinSpaceApi()
25+
fetch_result = FetchResult(
26+
data={
27+
'chain_stats': {
28+
'funded_txo_sum': 100,
29+
'spent_txo_sum': 100,
30+
}
31+
}
32+
)
33+
result = api.parse_balances(fetch_result)
34+
assert result.data is None
35+
36+
37+
def test_parse_empty_response():
38+
api = LitecoinSpaceApi()
39+
fetch_result = FetchResult(data=None)
40+
result = api.parse_balances(fetch_result)
41+
assert result.data is None
42+
43+
44+
@pytest.fixture
45+
def litecoinspace_balance_response():
46+
return read_file('data/litecoinspace_balance_response.json')

blockapi/v2/api/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from blockapi.v2.api.bitaps import BitapsLitecoinApi
12
from blockapi.v2.api.blockchain_info import BlockchainInfoApi
23
from blockapi.v2.api.blockchainos import BlockchainosApi
34
from blockapi.v2.api.blockchair import (
@@ -9,6 +10,7 @@
910
from blockapi.v2.api.debank import DebankApi, DebankApp, DebankPrediction
1011
from blockapi.v2.api.ethplorer import EthplorerApi
1112
from blockapi.v2.api.haskoin import HaskoinApi
13+
from blockapi.v2.api.litecoinspace import LitecoinSpaceApi
1214
from blockapi.v2.api.optimistic_etherscan import OptimismEtherscanApi
1315
from blockapi.v2.api.perpetual import PerpetualApi, perp_contract_address
1416
from blockapi.v2.api.solana import SolanaApi, SolscanApi

blockapi/v2/api/bitaps.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from blockapi.v2.base import BalanceMixin, BlockchainApi
2+
from blockapi.v2.coins import COIN_LTC
3+
from blockapi.v2.models import (
4+
ApiOptions,
5+
AssetType,
6+
BalanceItem,
7+
Blockchain,
8+
FetchResult,
9+
ParseResult,
10+
)
11+
12+
13+
class BitapsLitecoinApi(BlockchainApi, BalanceMixin):
14+
"""
15+
Coin: Litecoin
16+
API docs: https://developer.bitaps.com/
17+
Explorer: https://ltc.bitaps.com
18+
"""
19+
20+
coin = COIN_LTC
21+
api_options = ApiOptions(
22+
blockchain=Blockchain.LITECOIN,
23+
base_url='https://api.bitaps.com/ltc/v1/',
24+
rate_limit=0.1,
25+
)
26+
27+
supported_requests = {
28+
'get_balance': 'blockchain/address/state/{address}',
29+
}
30+
31+
def fetch_balances(self, address: str) -> FetchResult:
32+
return self.get_data('get_balance', address=address)
33+
34+
def parse_balances(self, fetch_result: FetchResult) -> ParseResult:
35+
if not fetch_result.data:
36+
return ParseResult()
37+
38+
data = fetch_result.data.get('data', {})
39+
balance_raw = data.get('balance')
40+
41+
if not balance_raw:
42+
return ParseResult()
43+
44+
return ParseResult(
45+
data=[
46+
BalanceItem.from_api(
47+
balance_raw=balance_raw,
48+
coin=self.coin,
49+
asset_type=AssetType.AVAILABLE,
50+
raw=fetch_result.data,
51+
)
52+
]
53+
)

blockapi/v2/api/litecoinspace.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from blockapi.v2.base import BalanceMixin, BlockchainApi
2+
from blockapi.v2.coins import COIN_LTC
3+
from blockapi.v2.models import (
4+
ApiOptions,
5+
AssetType,
6+
BalanceItem,
7+
Blockchain,
8+
FetchResult,
9+
ParseResult,
10+
)
11+
12+
13+
class LitecoinSpaceApi(BlockchainApi, BalanceMixin):
14+
"""
15+
Coin: Litecoin
16+
API docs: https://litecoinspace.org/docs/api/rest
17+
Explorer: https://litecoinspace.org
18+
"""
19+
20+
coin = COIN_LTC
21+
api_options = ApiOptions(
22+
blockchain=Blockchain.LITECOIN,
23+
base_url='https://litecoinspace.org',
24+
rate_limit=0.2,
25+
)
26+
27+
supported_requests = {
28+
'get_balance': '/api/address/{address}',
29+
}
30+
31+
def fetch_balances(self, address: str) -> FetchResult:
32+
return self.get_data('get_balance', address=address)
33+
34+
def parse_balances(self, fetch_result: FetchResult) -> ParseResult:
35+
if not fetch_result.data:
36+
return ParseResult()
37+
38+
chain_stats = fetch_result.data.get('chain_stats', {})
39+
funded = chain_stats.get('funded_txo_sum', 0)
40+
spent = chain_stats.get('spent_txo_sum', 0)
41+
balance_raw = funded - spent
42+
43+
if not balance_raw:
44+
return ParseResult()
45+
46+
return ParseResult(
47+
data=[
48+
BalanceItem.from_api(
49+
balance_raw=balance_raw,
50+
coin=self.coin,
51+
asset_type=AssetType.AVAILABLE,
52+
raw=fetch_result.data,
53+
)
54+
]
55+
)

0 commit comments

Comments
 (0)