Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions blockapi/v2/api/blockchair.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from abc import ABC
from typing import Iterable, List

from blockapi.v2.base import BalanceMixin, BlockchainApi, ITransactions
from blockapi.v2.base import BalanceMixin, BlockchainApi, ISleepProvider, ITransactions
from blockapi.v2.coins import COIN_BTC, COIN_DOGE, COIN_LTC
from blockapi.v2.models import (
ApiOptions,
Expand All @@ -19,8 +19,8 @@


class BlockchairApi(BlockchainApi, BalanceMixin, ITransactions, ABC):
def __init__(self, offset=0, limit=10):
super().__init__()
def __init__(self, offset=0, limit=10, sleep_provider: ISleepProvider = None):
super().__init__(sleep_provider=sleep_provider)
self._offset = offset
self._limit = limit

Expand Down Expand Up @@ -194,23 +194,29 @@ def _parse_transaction(self, address: str, tx: dict) -> TransactionItem:

class BlockchairBitcoinApi(BlockchairApi):
api_options = ApiOptions(
blockchain=Blockchain.BITCOIN, base_url='https://api.blockchair.com/bitcoin/'
blockchain=Blockchain.BITCOIN,
base_url='https://api.blockchair.com/bitcoin/',
rate_limit=1,
)

coin = COIN_BTC


class BlockchairDogecoinApi(BlockchairApi):
api_options = ApiOptions(
blockchain=Blockchain.DOGECHAIN, base_url='https://api.blockchair.com/dogecoin/'
blockchain=Blockchain.DOGECHAIN,
base_url='https://api.blockchair.com/dogecoin/',
rate_limit=1,
)

coin = COIN_DOGE


class BlockchairLitecoinApi(BlockchairApi):
api_options = ApiOptions(
blockchain=Blockchain.LITECOIN, base_url='https://api.blockchair.com/litecoin/'
blockchain=Blockchain.LITECOIN,
base_url='https://api.blockchair.com/litecoin/',
rate_limit=1,
)

coin = COIN_LTC
5 changes: 3 additions & 2 deletions blockapi/v2/api/cosmos.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from requests import Session

from blockapi.utils.num import to_decimal
from blockapi.v2.base import ApiOptions, BlockchainApi, IBalance
from blockapi.v2.base import ApiOptions, BlockchainApi, IBalance, ISleepProvider
from blockapi.v2.coins import COIN_ATOM, COIN_CELESTIA, COIN_DYDX, COIN_OSMOSIS
from blockapi.v2.models import AssetType, BalanceItem, Blockchain, Coin, CoinInfo

Expand Down Expand Up @@ -102,8 +102,9 @@ def __init__(
self,
tokens_map: defaultdict[str, dict] = None,
enable_token_mapping=True,
sleep_provider: ISleepProvider = None,
):
super().__init__()
super().__init__(sleep_provider=sleep_provider)
self._tokens_map = tokens_map
self.enable_token_mapping = enable_token_mapping

Expand Down
6 changes: 3 additions & 3 deletions blockapi/v2/api/covalenth/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from eth_utils import to_checksum_address

from blockapi.v2.base import BlockchainApi, IBalance
from blockapi.v2.base import BlockchainApi, IBalance, ISleepProvider
from blockapi.v2.models import BalanceItem, Coin, CoinInfo

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -37,8 +37,8 @@ def coin(self):
'get_balance': '/v1/{chain_id}/address/{address}/balances_v2/'
}

def __init__(self, api_key: str):
super().__init__(api_key)
def __init__(self, api_key: str, sleep_provider: ISleepProvider = None):
super().__init__(api_key, sleep_provider=sleep_provider)

# Set http basic auth for requests.
self._session.auth = (api_key, "")
Expand Down
16 changes: 9 additions & 7 deletions blockapi/v2/api/debank.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,28 @@
BalanceMixin,
CustomizableBlockchainApi,
IPortfolio,
ISleepProvider,
)
from blockapi.v2.blockchain_mapping import get_blockchain_from_debank_chain
from blockapi.v2.coin_mapping import symbol_to_coin_map
from blockapi.v2.models import (
AssetType,
BalanceItem,
Blockchain,
Coin,
CoingeckoId,
CoinInfo,
DebankApp,
DebankModelApp,
DebankModelAppPortfolioItem,
DebankModelPredictionDetail,
DebankPrediction,
FetchResult,
ParseResult,
Pool,
PoolInfo,
Protocol,
DebankApp,
DebankPrediction,
DebankModelAppPortfolioItem,
DebankModelApp,
DebankModelPredictionDetail,
)
from blockapi.v2.coin_mapping import symbol_to_coin_map

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -733,8 +734,9 @@ def __init__(
is_all: bool,
protocol_cache: Optional[DebankProtocolCache] = None,
base_url: Optional[str] = None,
sleep_provider: ISleepProvider = None,
):
super().__init__(base_url=base_url)
super().__init__(base_url=base_url, sleep_provider=sleep_provider)

self._is_all = bool(is_all)
self._headers = {'AccessKey': api_key}
Expand Down
6 changes: 3 additions & 3 deletions blockapi/v2/api/ethplorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from eth_utils import to_checksum_address

from blockapi.v2.base import ApiOptions, BalanceMixin, BlockchainApi
from blockapi.v2.base import ApiOptions, BalanceMixin, BlockchainApi, ISleepProvider
from blockapi.v2.coins import COIN_ETH
from blockapi.v2.models import (
BalanceItem,
Expand Down Expand Up @@ -30,8 +30,8 @@ class EthplorerApi(BlockchainApi, BalanceMixin):

supported_requests = {'get_info': '/getAddressInfo/{address}?apiKey={api_key}'}

def __init__(self, api_key: str = 'freekey'):
super().__init__(api_key)
def __init__(self, api_key: str = 'freekey', sleep_provider: ISleepProvider = None):
super().__init__(api_key, sleep_provider=sleep_provider)

def fetch_balances(self, address: str) -> FetchResult:
return self.get_data(
Expand Down
25 changes: 16 additions & 9 deletions blockapi/v2/api/nft/magic_eden.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
from decimal import Decimal
from typing import Iterable, Optional, Tuple

from blockapi.v2.base import BlockchainApi, INftParser, INftProvider
from blockapi.v2.base import (
BlockchainApi,
INftParser,
INftProvider,
ISleepProvider,
SleepProvider,
)
from blockapi.v2.coins import COIN_SOL
from blockapi.v2.models import (
ApiOptions,
Expand Down Expand Up @@ -46,9 +52,10 @@ class MagicEdenApi(BlockchainApi, INftProvider, INftParser):

coin_map = NotImplemented

def __init__(self, sleep_provider, max_listings=500, max_offers=500):
super().__init__()
self._sleep_provider = sleep_provider
def __init__(
self, sleep_provider: ISleepProvider = None, max_listings=500, max_offers=500
):
super().__init__(sleep_provider=sleep_provider or SleepProvider())

self.max_offers = max_offers
if max_listings > 15000:
Expand All @@ -67,7 +74,7 @@ def fetch_nfts(self, address: str) -> FetchResult:
items = []

while True:
self._sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
self.sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
data = self.get_data(
'get_nfts',
address=address,
Expand Down Expand Up @@ -131,7 +138,7 @@ def _parse_supply(s: str):

def fetch_collection(self, collection: str) -> FetchResult:
while True:
self._sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
self.sleep_provider.sleep(self.base_url, self.api_options.rate_limit)

data = self.get_data(
'get_collection',
Expand Down Expand Up @@ -186,7 +193,7 @@ def fetch_offers(
items = []

while True:
self._sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
self.sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
logger.info(f'get_pools: {collection} offset={offset} limit={limit}')
data = self.get_data(
'get_pools', slug=collection, offset=offset, limit=limit
Expand Down Expand Up @@ -283,7 +290,7 @@ def fetch_listings(
items = []

while True:
self._sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
self.sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
data = self.get_data(
'get_listings',
slug=collection,
Expand Down Expand Up @@ -396,7 +403,7 @@ def _should_retry(self, data: FetchResult) -> bool:
)
if retry:
logger.warning('Service unavailable - will retry after long sleep')
self._sleep_provider.sleep(self.base_url, seconds=60)
self.sleep_provider.sleep(self.base_url, seconds=60)
return True

return False
Expand Down
7 changes: 3 additions & 4 deletions blockapi/v2/api/nft/opensea.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,14 @@ def __init__(
max_listings=500,
max_offers=500,
):
super().__init__(api_key)
super().__init__(api_key, sleep_provider=sleep_provider or SleepProvider())

self._blockchain = blockchain
self._opensea_chain = self.supported_blockchains_map.get(blockchain)
if not self._opensea_chain:
raise ApiException(f"Blockchain '{blockchain.value}' is not supported")

self._headers = {'accept': 'application/json', 'x-api-key': api_key}
self._sleep_provider = sleep_provider or SleepProvider()
self._limit = limit

self.max_listings = max_listings
Expand Down Expand Up @@ -264,7 +263,7 @@ def _yield_fetch_data(
item_count = 0

while True:
self._sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
self.sleep_provider.sleep(self.base_url, self.api_options.rate_limit)
page_count += 1
logger.debug(f'Fetching page {page_count} of {key} from {cursor}')
fetched, next_cursor = fetch_method(key, cursor)
Expand Down Expand Up @@ -659,7 +658,7 @@ def _should_retry(self, data):

logger.warning(f'Service unavailable - will retry after {seconds}s sleep')

self._sleep_provider.sleep(self.base_url, seconds=seconds)
self.sleep_provider.sleep(self.base_url, seconds=seconds)
return True

return False
12 changes: 9 additions & 3 deletions blockapi/v2/api/optimistic_etherscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
from requests import Response

from blockapi.utils.user_agent import get_random_user_agent
from blockapi.v2.base import ApiException, ApiOptions, BalanceMixin, BlockchainApi
from blockapi.v2.base import (
ApiException,
ApiOptions,
BalanceMixin,
BlockchainApi,
ISleepProvider,
)
from blockapi.v2.coins import COIN_ETH
from blockapi.v2.models import BalanceItem, Blockchain, FetchResult, ParseResult

Expand All @@ -25,8 +31,8 @@ class OptimismEtherscanApi(BlockchainApi, BalanceMixin):
'get_balance': '?module=account&action=balance&address={address}&tag=latest&apikey={api_key}'
}

def __init__(self, api_key: str = ''):
super().__init__(api_key)
def __init__(self, api_key: str = '', sleep_provider: ISleepProvider = None):
super().__init__(api_key, sleep_provider=sleep_provider)

def _parse_eth_balance(self, response: Dict) -> BalanceItem:
return BalanceItem.from_api(
Expand Down
5 changes: 3 additions & 2 deletions blockapi/v2/api/perpetual/perpetual.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
BalanceMixin,
CustomizableBlockchainApi,
IBalance,
ISleepProvider,
)
from blockapi.v2.coins import COIN_PERP
from blockapi.v2.models import (
Expand Down Expand Up @@ -217,8 +218,8 @@ class PerpetualApi(CustomizableBlockchainApi, BalanceMixin):
blockchain=Blockchain.ETHEREUM, base_url=None, rate_limit=0.2
)

def __init__(self, base_url: str) -> None:
super().__init__(base_url=base_url)
def __init__(self, base_url: str, sleep_provider: ISleepProvider = None) -> None:
super().__init__(base_url=base_url, sleep_provider=sleep_provider)

def fetch_balances(self, address: str) -> FetchResult:
try:
Expand Down
12 changes: 10 additions & 2 deletions blockapi/v2/api/solana.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
BlockchainApi,
CustomizableBlockchainApi,
InvalidAddressException,
ISleepProvider,
)
from blockapi.v2.coins import COIN_SOL
from blockapi.v2.models import (
Expand Down Expand Up @@ -65,6 +66,7 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):
api_options = ApiOptions(
blockchain=Blockchain.SOLANA,
base_url='https://api.mainnet-beta.solana.com/',
rate_limit=1,
start_offset=0,
max_items_per_page=1000,
page_offset_step=1,
Expand All @@ -85,8 +87,13 @@ class SolanaApi(CustomizableBlockchainApi, BalanceMixin):

# ── Initialization ─────────────────────────────────────────

def __init__(self, base_url: Optional[str] = None, include_nfts: bool = False):
super().__init__(base_url)
def __init__(
self,
base_url: Optional[str] = None,
include_nfts: bool = False,
sleep_provider: ISleepProvider = None,
):
super().__init__(base_url, sleep_provider=sleep_provider)
self.include_nfts = include_nfts
self._request_id = 0

Expand Down Expand Up @@ -419,6 +426,7 @@ class SolscanApi(BlockchainApi):
api_options = ApiOptions(
blockchain=Blockchain.SOLANA,
base_url='https://api.solscan.io/',
rate_limit=1,
start_offset=0,
max_items_per_page=1000,
page_offset_step=1,
Expand Down
Loading