blockapi 2.11.1__tar.gz → 2.11.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. {blockapi-2.11.1 → blockapi-2.11.3}/PKG-INFO +1 -1
  2. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_cosmos.py +2 -2
  3. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_solana.py +148 -7
  4. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/cosmos.py +3 -3
  5. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/solana.py +46 -17
  6. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi.egg-info/PKG-INFO +1 -1
  7. {blockapi-2.11.1 → blockapi-2.11.3}/setup.py +1 -1
  8. {blockapi-2.11.1 → blockapi-2.11.3}/LICENSE.md +0 -0
  9. {blockapi-2.11.1 → blockapi-2.11.3}/README.md +0 -0
  10. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/__init__.py +0 -0
  11. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/services.py +0 -0
  12. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/__init__.py +0 -0
  13. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/test_blockapi.py +0 -0
  14. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/test_num.py +0 -0
  15. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/test_random_user_agent.py +0 -0
  16. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/__init__.py +0 -0
  17. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/__init__.py +0 -0
  18. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/conftest.py +0 -0
  19. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/covalenth/__init__.py +0 -0
  20. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/covalenth/test_ethereum.py +0 -0
  21. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/fake_sleep_provider.py +0 -0
  22. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/nft/__init__.py +0 -0
  23. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/nft/test_magic_eden.py +0 -0
  24. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/nft/test_opensea.py +0 -0
  25. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/nft/test_simple_hash.py +0 -0
  26. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/nft/test_unisat.py +0 -0
  27. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_bitaps_ltc.py +0 -0
  28. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_blockchain_info.py +0 -0
  29. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_blockchainos.py +0 -0
  30. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_blockchair_btc.py +0 -0
  31. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_blockchair_doge.py +0 -0
  32. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_blockchair_ltc.py +0 -0
  33. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_ethplorer.py +0 -0
  34. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_haskoin.py +0 -0
  35. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_litecoinspace.py +0 -0
  36. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_multisources.py +0 -0
  37. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_optimistic_etherscan.py +0 -0
  38. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_subscan_polkadot.py +0 -0
  39. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_sui.py +0 -0
  40. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_terra.py +0 -0
  41. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_trezor_btc.py +0 -0
  42. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/api/test_trezor_zec.py +0 -0
  43. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/test_base.py +0 -0
  44. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/test_blockchain_api.py +0 -0
  45. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/test_blockchain_mapping.py +0 -0
  46. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/test_data.py +0 -0
  47. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/test_enumerate_classes.py +0 -0
  48. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/test_generic.py +0 -0
  49. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test/v2/test_models.py +0 -0
  50. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/test_data.py +0 -0
  51. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/utils/__init__.py +0 -0
  52. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/utils/address.py +0 -0
  53. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/utils/datetime.py +0 -0
  54. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/utils/num.py +0 -0
  55. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/utils/user_agent.py +0 -0
  56. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/__init__.py +0 -0
  57. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/__init__.py +0 -0
  58. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/bitaps.py +0 -0
  59. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/blockchain_info.py +0 -0
  60. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/blockchainos.py +0 -0
  61. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/blockchair.py +0 -0
  62. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/__init__.py +0 -0
  63. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/arbitrum.py +0 -0
  64. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/astar.py +0 -0
  65. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/avalanche.py +0 -0
  66. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/axie.py +0 -0
  67. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/base.py +0 -0
  68. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/binance_smart_chain.py +0 -0
  69. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/ethereum.py +0 -0
  70. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/fantom.py +0 -0
  71. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/heco.py +0 -0
  72. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/iotex.py +0 -0
  73. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/klaytn.py +0 -0
  74. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/moonbeam.py +0 -0
  75. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/palm.py +0 -0
  76. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/polygon.py +0 -0
  77. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/covalenth/rsk.py +0 -0
  78. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/debank.py +0 -0
  79. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/debank_maps.py +0 -0
  80. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/ethplorer.py +0 -0
  81. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/haskoin.py +0 -0
  82. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/litecoinspace.py +0 -0
  83. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/nft/__init__.py +0 -0
  84. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/nft/magic_eden.py +0 -0
  85. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/nft/opensea.py +0 -0
  86. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/nft/simple_hash.py +0 -0
  87. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/nft/unisat.py +0 -0
  88. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/optimistic_etherscan.py +0 -0
  89. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/subscan.py +0 -0
  90. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/sui.py +0 -0
  91. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/terra.py +0 -0
  92. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/api/trezor.py +0 -0
  93. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/base.py +0 -0
  94. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/blockchain_mapping.py +0 -0
  95. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/coin_mapping.py +0 -0
  96. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/coins.py +0 -0
  97. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi/v2/models.py +0 -0
  98. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi.egg-info/SOURCES.txt +0 -0
  99. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi.egg-info/dependency_links.txt +0 -0
  100. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi.egg-info/requires.txt +0 -0
  101. {blockapi-2.11.1 → blockapi-2.11.3}/blockapi.egg-info/top_level.txt +0 -0
  102. {blockapi-2.11.1 → blockapi-2.11.3}/pyproject.toml +0 -0
  103. {blockapi-2.11.1 → blockapi-2.11.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blockapi
3
- Version: 2.11.1
3
+ Version: 2.11.3
4
4
  Summary: BlockAPI library
5
5
  Home-page: https://github.com/crypkit/blockapi
6
6
  Author: Devmons s.r.o.
@@ -228,7 +228,7 @@ def test_cosmos_get_balance():
228
228
  def test_osmosis_get_balance():
229
229
  api = CosmosOsmosisApi()
230
230
  balances = api.get_balance('osmo1aff76avnwpnk02wxkc6n5xnwasjkgekazvl8xj')
231
- assert len(balances) == 2
231
+ assert len(balances) == 4
232
232
  assert all([balance.coin != UNKNOWN for balance in balances])
233
233
 
234
234
 
@@ -237,7 +237,7 @@ def test_osmosis_get_balance():
237
237
  def test_dydx_get_balance():
238
238
  api = CosmosDydxApi()
239
239
  balances = api.get_balance('dydx1aff76avnwpnk02wxkc6n5xnwasjkgekarwznsh')
240
- assert len(balances) == 4
240
+ assert len(balances) == 1
241
241
  assert all([balance.coin != UNKNOWN for balance in balances])
242
242
 
243
243
 
@@ -1,11 +1,13 @@
1
+ import json
1
2
  from decimal import Decimal
2
- from unittest.mock import patch
3
+ from unittest.mock import call, patch
3
4
 
4
5
  import pytest
5
6
  from requests_mock import ANY, Mocker
6
7
 
7
8
  from blockapi.test.v2.api.conftest import read_file
8
9
  from blockapi.v2.api import SolanaApi, SolscanApi
10
+ from blockapi.v2.base import ApiException
9
11
  from blockapi.v2.models import (
10
12
  AssetType,
11
13
  BalanceItem,
@@ -74,14 +76,26 @@ def test_use_base_url():
74
76
  assert api.base_url == 'https://api.mainnet-beta.solana.com/'
75
77
 
76
78
 
77
- def test_use_base_url_in_post(
79
+ @pytest.mark.parametrize(
80
+ ('rpc_url', 'uses_v2_staking'),
81
+ [
82
+ ('https://mainnet.helius-rpc.com/', True),
83
+ ('https://proxy/solana/', False),
84
+ ],
85
+ )
86
+ def test_get_balance_supports_helius_and_legacy_staking_responses(
78
87
  sol_balance_response,
79
88
  token_accounts_response,
80
89
  das_asset_batch_response,
81
90
  staked_solana_response,
91
+ rpc_url,
92
+ uses_v2_staking,
82
93
  ):
83
94
  test_addr = '5PjMxaijeVVQtuEzxK2NxyJeWwUbpTsi2uXuZ653WoHu'
84
95
  empty_token_accounts = '{"jsonrpc":"2.0","result":{"context":{"apiVersion":"1.17.34","slot":268207149},"value":[]},"id":1}'
96
+ staking_response = json.loads(staked_solana_response)
97
+ if not uses_v2_staking:
98
+ staking_response['result'] = staking_response['result']['accounts']
85
99
 
86
100
  iterator = iter(
87
101
  [
@@ -89,19 +103,29 @@ def test_use_base_url_in_post(
89
103
  token_accounts_response,
90
104
  empty_token_accounts,
91
105
  das_asset_batch_response,
92
- staked_solana_response,
106
+ json.dumps(staking_response),
93
107
  ]
94
108
  )
95
109
 
96
110
  def get_text(*args, **kwargs):
97
- assert args[0].url == 'https://proxy/solana/'
111
+ assert args[0].url == rpc_url
98
112
  data = next(iterator)
99
113
  return data
100
114
 
101
115
  with Mocker() as m:
102
- m.post(ANY, text=get_text),
103
- api = SolanaApi(base_url='https://proxy/solana/')
104
- api.get_balance(test_addr)
116
+ m.post(ANY, text=get_text)
117
+ api = SolanaApi(base_url=rpc_url)
118
+ balances = api.get_balance(test_addr)
119
+
120
+ staking_balances = {
121
+ balance.asset_type: balance.balance_raw
122
+ for balance in balances
123
+ if balance.asset_type in {AssetType.STAKED, AssetType.LOCKED}
124
+ }
125
+ assert staking_balances == {
126
+ AssetType.STAKED: Decimal('179062913955311'),
127
+ AssetType.LOCKED: Decimal('424045085255'),
128
+ }
105
129
 
106
130
 
107
131
  def test_build_coin_from_das_asset():
@@ -237,6 +261,123 @@ def test_parse_staked_balance_skips_undelegated():
237
261
  assert result.asset_type == AssetType.STAKED
238
262
 
239
263
 
264
+ def test_fetch_staked_sol_uses_v2_pagination():
265
+ api = SolanaApi(base_url='https://mainnet.helius-rpc.com/')
266
+ address = '5PjMxaijeVVQtuEzxK2NxyJeWwUbpTsi2uXuZ653WoHu'
267
+ first_account = {'pubkey': 'first'}
268
+ second_account = {'pubkey': 'second'}
269
+
270
+ with patch.object(
271
+ api,
272
+ '_request',
273
+ side_effect=[
274
+ {
275
+ 'jsonrpc': '2.0',
276
+ 'id': 1,
277
+ 'result': {
278
+ 'accounts': [first_account],
279
+ 'paginationKey': 'next-page',
280
+ },
281
+ },
282
+ {
283
+ 'jsonrpc': '2.0',
284
+ 'id': 2,
285
+ 'result': {
286
+ 'accounts': [],
287
+ 'paginationKey': 'last-page',
288
+ },
289
+ },
290
+ {
291
+ 'jsonrpc': '2.0',
292
+ 'id': 3,
293
+ 'result': {
294
+ 'accounts': [second_account],
295
+ 'paginationKey': None,
296
+ },
297
+ },
298
+ ],
299
+ ) as request:
300
+ response = api._fetch_staked_sol(address)
301
+
302
+ config = {
303
+ 'filters': [
304
+ {
305
+ 'memcmp': {
306
+ 'offset': api.STAKE_AUTHORITY_OFFSET,
307
+ 'bytes': address,
308
+ 'encoding': 'base58',
309
+ }
310
+ }
311
+ ],
312
+ 'encoding': 'jsonParsed',
313
+ 'commitment': 'finalized',
314
+ 'limit': api.HELIUS_PROGRAM_ACCOUNTS_PAGE_SIZE,
315
+ }
316
+ assert request.call_args_list == [
317
+ call(
318
+ method='getProgramAccountsV2',
319
+ params=[api.STAKE_PROGRAM_ID, config],
320
+ ),
321
+ call(
322
+ method='getProgramAccountsV2',
323
+ params=[
324
+ api.STAKE_PROGRAM_ID,
325
+ {**config, 'paginationKey': 'next-page'},
326
+ ],
327
+ ),
328
+ call(
329
+ method='getProgramAccountsV2',
330
+ params=[
331
+ api.STAKE_PROGRAM_ID,
332
+ {**config, 'paginationKey': 'last-page'},
333
+ ],
334
+ ),
335
+ ]
336
+ assert response == {
337
+ 'jsonrpc': '2.0',
338
+ 'id': 3,
339
+ 'result': [first_account, second_account],
340
+ }
341
+
342
+
343
+ def test_fetch_staked_sol_rejects_repeated_pagination_key():
344
+ api = SolanaApi(base_url='https://mainnet.helius-rpc.com/')
345
+ repeated_page = {
346
+ 'result': {
347
+ 'accounts': [],
348
+ 'paginationKey': 'same-page',
349
+ }
350
+ }
351
+ terminal_page = {
352
+ 'result': {
353
+ 'accounts': [],
354
+ 'paginationKey': None,
355
+ }
356
+ }
357
+
358
+ with patch.object(
359
+ api,
360
+ '_request',
361
+ side_effect=[repeated_page, repeated_page, terminal_page],
362
+ ) as request:
363
+ with pytest.raises(ApiException, match='repeated pagination key'):
364
+ api._fetch_staked_sol('address')
365
+
366
+ assert request.call_count == 2
367
+
368
+
369
+ def test_fetch_staked_sol_uses_legacy_method_for_non_helius_rpc():
370
+ api = SolanaApi()
371
+
372
+ with patch.object(api, '_request', return_value={'result': []}) as request:
373
+ response = api._fetch_staked_sol('address')
374
+
375
+ request.assert_called_once()
376
+ assert request.call_args.kwargs['method'] == 'getProgramAccounts'
377
+ assert 'limit' not in request.call_args.kwargs['params'][1]
378
+ assert response == {'result': []}
379
+
380
+
240
381
  def test_das_cache_stores_sentinel_for_unknown_mint():
241
382
  api = SolanaApi()
242
383
  unknown_mint = 'UnknownMint111111111111111111111111111111111'
@@ -252,7 +252,7 @@ class CosmosOsmosisApi(CosmosApiBase):
252
252
  TOKENS_MAP_BLOCKCHAIN_KEY = "osmosis"
253
253
  api_options = ApiOptions(
254
254
  blockchain=Blockchain.OSMOSIS,
255
- base_url='https://lcd-osmosis.cosmostation.io/',
255
+ base_url='https://osmosis-mainnet-api.itrocket.net/',
256
256
  rate_limit=CosmosApiBase.API_BASE_RATE_LIMIT,
257
257
  )
258
258
 
@@ -262,7 +262,7 @@ class CosmosDydxApi(CosmosApiBase):
262
262
  TOKENS_MAP_BLOCKCHAIN_KEY = "dydx"
263
263
  api_options = ApiOptions(
264
264
  blockchain=Blockchain.DYDX,
265
- base_url='https://lcd-dydx.cosmostation.io/',
265
+ base_url='https://dydx-rest.publicnode.com/',
266
266
  rate_limit=CosmosApiBase.API_BASE_RATE_LIMIT,
267
267
  )
268
268
 
@@ -272,6 +272,6 @@ class CosmosCelestiaApi(CosmosApiBase):
272
272
  TOKENS_MAP_BLOCKCHAIN_KEY = "celestia"
273
273
  api_options = ApiOptions(
274
274
  blockchain=Blockchain.CELESTIA,
275
- base_url='https://lcd-celestia.cosmostation.io/',
275
+ base_url='https://celestia-mainnet-api.itrocket.net/',
276
276
  rate_limit=CosmosApiBase.API_BASE_RATE_LIMIT,
277
277
  )
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import logging
3
3
  from typing import Optional, Union
4
+ from urllib.parse import urlparse
4
5
 
5
6
  from cytoolz import reduceby
6
7
  from requests import Response
@@ -79,6 +80,8 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):
79
80
  TOKEN_2022_PROGRAM_ID = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'
80
81
  STAKE_PROGRAM_ID = 'Stake11111111111111111111111111111111111111'
81
82
  STAKE_AUTHORITY_OFFSET = 44
83
+ HELIUS_RPC_DOMAIN = 'helius-rpc.com'
84
+ HELIUS_PROGRAM_ACCOUNTS_PAGE_SIZE = 5000
82
85
  DAS_BATCH_SIZE = 1000
83
86
  _JSONRPC_INVALID_PARAMS = -32602
84
87
 
@@ -314,25 +317,51 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):
314
317
 
315
318
  def _fetch_staked_sol(self, address: str) -> dict:
316
319
  """Fetch staked SOL accounts for a given address."""
317
- return self._request(
318
- method='getProgramAccounts',
319
- params=[
320
- self.STAKE_PROGRAM_ID,
320
+ config = {
321
+ 'filters': [
321
322
  {
322
- 'filters': [
323
- {
324
- 'memcmp': {
325
- 'offset': self.STAKE_AUTHORITY_OFFSET,
326
- 'bytes': address,
327
- 'encoding': 'base58',
328
- }
329
- }
330
- ],
331
- 'encoding': 'jsonParsed',
332
- 'commitment': 'finalized',
333
- },
323
+ 'memcmp': {
324
+ 'offset': self.STAKE_AUTHORITY_OFFSET,
325
+ 'bytes': address,
326
+ 'encoding': 'base58',
327
+ }
328
+ }
334
329
  ],
330
+ 'encoding': 'jsonParsed',
331
+ 'commitment': 'finalized',
332
+ }
333
+
334
+ hostname = urlparse(self.base_url).hostname or ''
335
+ is_helius_rpc = hostname == self.HELIUS_RPC_DOMAIN or hostname.endswith(
336
+ f'.{self.HELIUS_RPC_DOMAIN}'
335
337
  )
338
+ if not is_helius_rpc:
339
+ return self._request(
340
+ method='getProgramAccounts',
341
+ params=[self.STAKE_PROGRAM_ID, config],
342
+ )
343
+
344
+ config['limit'] = self.HELIUS_PROGRAM_ACCOUNTS_PAGE_SIZE
345
+ accounts = []
346
+ seen_pagination_keys = set()
347
+
348
+ while True:
349
+ response = self._request(
350
+ method='getProgramAccountsV2',
351
+ params=[self.STAKE_PROGRAM_ID, config],
352
+ )
353
+ page = response['result']
354
+ accounts.extend(page['accounts'])
355
+
356
+ pagination_key = page.get('paginationKey')
357
+ if pagination_key is None:
358
+ response['result'] = accounts
359
+ return response
360
+ if pagination_key in seen_pagination_keys:
361
+ raise ApiException('Solana RPC returned a repeated pagination key')
362
+
363
+ seen_pagination_keys.add(pagination_key)
364
+ config = {**config, 'paginationKey': pagination_key}
336
365
 
337
366
  # ── Balance parsing ────────────────────────────────────────
338
367
 
@@ -373,7 +402,7 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):
373
402
  def _parse_rent_reserve(
374
403
  self, staked_sol: BalanceItem, raw_staked_sol: dict
375
404
  ) -> BalanceItem:
376
- """Parse rent reserve from getProgramAccounts response.
405
+ """Parse rent reserve from a program accounts response.
377
406
 
378
407
  Uses result[].account.lamports already returned by _fetch_staked_sol,
379
408
  avoiding a separate getMultipleAccounts call.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blockapi
3
- Version: 2.11.1
3
+ Version: 2.11.3
4
4
  Summary: BlockAPI library
5
5
  Home-page: https://github.com/crypkit/blockapi
6
6
  Author: Devmons s.r.o.
@@ -6,7 +6,7 @@ with open("README.md", "r") as f:
6
6
 
7
7
  PACKAGES = find_packages(where='.')
8
8
 
9
- __version__ = "2.11.1"
9
+ __version__ = "2.11.3"
10
10
 
11
11
  setuptools.setup(
12
12
  name='blockapi',
File without changes
File without changes
File without changes
File without changes
File without changes