diff --git a/.github/workflows/blockapi-ci.yml b/.github/workflows/blockapi-ci.yml index e8b80b27..5a4046b5 100644 --- a/.github/workflows/blockapi-ci.yml +++ b/.github/workflows/blockapi-ci.yml @@ -14,10 +14,13 @@ jobs: with: fetch-depth: 0 - - name: Set up Python 3.9 + - name: Set up Python 3.12.11 uses: actions/setup-python@v4 with: - python-version: '3.9' + python-version: '3.12.11' + + - name: Verify Python + run: python --version - name: Restore cache uses: actions/cache@v3 diff --git a/blockapi/services.py b/blockapi/services.py index ac7e28c1..5703a581 100644 --- a/blockapi/services.py +++ b/blockapi/services.py @@ -3,13 +3,10 @@ from datetime import datetime from time import sleep -import cfscrape import requests import blockapi -cfscrape.DEFAULT_CIPHERS += ':!SHA' - class Service(ABC): """General class for handling blockchain API services.""" @@ -34,13 +31,7 @@ def build_request_url(self, request_method, **params): return self.base_url def request( - self, - request_method, - with_rate_limit=False, - with_cloudflare=False, - body=None, - headers=None, - **params + self, request_method, with_rate_limit=False, body=None, headers=None, **params ): request_url = self.build_request_url(request_method, **params) @@ -53,10 +44,7 @@ def request( if not headers: headers = {} - if with_cloudflare: - reqobj = cfscrape.create_scraper() - else: - reqobj = requests + reqobj = requests if with_rate_limit and self.rate_limit: self.wait_for_next_request() diff --git a/blockapi/test/v2/api/cassettes/test_dissable_mapping.yaml b/blockapi/test/v2/api/cassettes/test_dissable_mapping.yaml new file mode 100644 index 00000000..6e13296c --- /dev/null +++ b/blockapi/test/v2/api/cassettes/test_dissable_mapping.yaml @@ -0,0 +1,186 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Referer: + - https://www.mintscan.io/ + User-Agent: + - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, + like Gecko) Chrome/50.0.2661.102 Safari/537.36 + method: GET + uri: https://lcd-dydx.cosmostation.io/cosmos/bank/v1beta1/balances/dydx1aff76avnwpnk02wxkc6n5xnwasjkgekarwznsh + response: + body: + string: '{"balances":[{"denom":"adydx","amount":"8342195962385344539"}],"pagination":{"next_key":null,"total":"1"}}' + headers: + Access-Control-Allow-Headers: + - DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range + Access-Control-Allow-Methods: + - GET, POST, OPTIONS + Access-Control-Expose-Headers: + - Content-Length,Content-Range + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Thu, 28 Aug 2025 13:12:14 GMT + Grpc-Metadata-Content-Type: + - application/grpc+cosmos-sdk-grpc-codec + Grpc-Metadata-X-Cosmos-Block-Height: + - '53991912' + Server: + - nginx + Transfer-Encoding: + - chunked + Vary: + - Accept-Encoding + X-Server-Time: + - '1756386734' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Referer: + - https://www.mintscan.io/ + User-Agent: + - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, + like Gecko) Chrome/50.0.2661.102 Safari/537.36 + method: GET + uri: https://lcd-dydx.cosmostation.io/cosmos/staking/v1beta1/delegations/dydx1aff76avnwpnk02wxkc6n5xnwasjkgekarwznsh?pagination.limit=1000 + response: + body: + string: '{"delegation_responses":[],"pagination":{"next_key":null,"total":"0"}}' + headers: + Access-Control-Allow-Headers: + - DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range + Access-Control-Allow-Methods: + - GET, POST, OPTIONS + Access-Control-Expose-Headers: + - Content-Length,Content-Range + Connection: + - keep-alive + Content-Length: + - '70' + Content-Type: + - application/json + Date: + - Thu, 28 Aug 2025 13:12:15 GMT + Grpc-Metadata-Content-Type: + - application/grpc+cosmos-sdk-grpc-codec + Grpc-Metadata-X-Cosmos-Block-Height: + - '53991912' + Server: + - nginx + X-Server-Time: + - '1756386735' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Referer: + - https://www.mintscan.io/ + User-Agent: + - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, + like Gecko) Chrome/50.0.2661.102 Safari/537.36 + method: GET + uri: https://lcd-dydx.cosmostation.io/cosmos/staking/v1beta1/delegators/dydx1aff76avnwpnk02wxkc6n5xnwasjkgekarwznsh/unbonding_delegations + response: + body: + string: '{"unbonding_responses":[],"pagination":{"next_key":null,"total":"0"}}' + headers: + Access-Control-Allow-Headers: + - DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range + Access-Control-Allow-Methods: + - GET, POST, OPTIONS + Access-Control-Expose-Headers: + - Content-Length,Content-Range + Connection: + - keep-alive + Content-Length: + - '69' + Content-Type: + - application/json + Date: + - Thu, 28 Aug 2025 13:12:16 GMT + Grpc-Metadata-Content-Type: + - application/grpc+cosmos-sdk-grpc-codec + Grpc-Metadata-X-Cosmos-Block-Height: + - '53991913' + Server: + - nginx + X-Server-Time: + - '1756386736' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Referer: + - https://www.mintscan.io/ + User-Agent: + - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, + like Gecko) Chrome/50.0.2661.102 Safari/537.36 + method: GET + uri: https://lcd-dydx.cosmostation.io/cosmos/distribution/v1beta1/delegators/dydx1aff76avnwpnk02wxkc6n5xnwasjkgekarwznsh/rewards + response: + body: + string: '{"rewards":[],"total":[]}' + headers: + Access-Control-Allow-Headers: + - DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range + Access-Control-Allow-Methods: + - GET, POST, OPTIONS + Access-Control-Expose-Headers: + - Content-Length,Content-Range + Connection: + - keep-alive + Content-Length: + - '25' + Content-Type: + - application/json + Date: + - Thu, 28 Aug 2025 13:12:16 GMT + Grpc-Metadata-Content-Type: + - application/grpc+cosmos-sdk-grpc-codec + Grpc-Metadata-X-Cosmos-Block-Height: + - '53991914' + Server: + - nginx + X-Server-Time: + - '1756386736' + status: + code: 200 + message: OK +version: 1 diff --git a/blockapi/test/v2/api/cassettes/test_fetch_metaplex_account.yaml b/blockapi/test/v2/api/cassettes/test_fetch_metaplex_account.yaml index b254e79b..13af1f11 100644 --- a/blockapi/test/v2/api/cassettes/test_fetch_metaplex_account.yaml +++ b/blockapi/test/v2/api/cassettes/test_fetch_metaplex_account.yaml @@ -14,22 +14,14 @@ interactions: Content-Type: - application/json User-Agent: - - python-requests/2.31.0 + - python-requests/2.32.5 method: POST uri: https://api.mainnet-beta.solana.com/ response: body: - string: !!binary | - H4sIAAAAAAAEA6pWyirOzysqSFayUjLSM1DSUSpKLS7NKVGyqlZKzs8rSa0AMxMLMsNSi4oz8/OU - rJQM9Qwt9IxMlXSUinPyS5SsjCzNDYwsjU1ManWUyhJzSlNBmlMSSxKVrKKVnLxM87MjgvJKQl1T - y0vdM0sD/T19w0sstCOzy9zyIs31XfLTMwIqq0wLy/3yLcrDC6ryM3KSvQOywj1dnHOM89LT3YtL - jJ1T3cpCtQMMyx3NKv18M3It0h0dHR0Dw/0MPF2z0/0znUIT3UPTA0Oz00M9vMqS3TNyIo2zQUpg - 2BvECAz1CwXRYOwLMsIpP8UjqDy5Kr/MxyglI8U9zDgyIrs02T2nNDIiKMMn1684yTgs28copzwq - z7csNNwwPCrbMDDQMCwwuSos3DfcsiQyNMgoyci0wLcqw9jf3c0tOM/JPcy9wC08x8s/NNewLCwv - JyfVLSMiKhBs6+An0kycHEFBM/hdOrxcGGhrq6SjlJRYnGpmohSro5RakZpcWpKYlJOqZJWWmFOc - qqOUk5hbkF9UUqxkZWhqaGhqZmCgo5RfnpdapGSllJtakliYVFERmlqUUmhkkZxlGJTkGJ4dGZhr - XJlUlZVklmiRVGJqaFFhWAzO5IDllbgW5CdnKFkZWpiYmJmbmBiYG5sbWJqaGpoZmuooFRckJqcq - WZkZmNfW6ihlpihZGdZyAQAzXeNRLQQAAA== + string: '{"jsonrpc":"2.0","result":{"context":{"apiVersion":"2.3.6","slot":363077074},"value":{"data":["BJ5okXRntUEewuGiuQOIMWt8+YkvFnY7/DoghPyz5qwNo8wWpzohlcKPjWIDCl3nggGst3CeFvU+P1wA6yNMhm8gAAAAQWN0IEkgOiBUaGUgQUkgUHJvcGhlY3kAAAAAAAAAAAAKAAAAQUNUAAAAAAAAAMgAAABodHRwczovL2dhdGV3YXkucGluYXRhLmNsb3VkL2lwZnMvUW1WZk1QQ1VQczVWMW9tYUR2b25pMzh3OGFFSnBGVGpFWlJOUm1vVnlleFhXZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf4BAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==","base64"],"executable":false,"lamports":5115600,"owner":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s","rentEpoch":18446744073709551615,"space":607}},"id":1} + + ' headers: access-control-allow-headers: - authorization, * @@ -46,7 +38,7 @@ interactions: content-type: - application/json; charset=utf-8 date: - - Tue, 22 Oct 2024 09:44:34 GMT + - Thu, 28 Aug 2025 13:00:35 GMT transfer-encoding: - chunked vary: @@ -62,11 +54,11 @@ interactions: x-ratelimit-endpoint-limit: - unlimited x-ratelimit-endpoint-remaining: - - '-778' + - '-2506' x-ratelimit-method-limit: - - '10' + - '30' x-ratelimit-method-remaining: - - '9' + - '29' x-ratelimit-pubsub-limit: - '5' x-ratelimit-pubsub-remaining: @@ -78,7 +70,7 @@ interactions: x-ratelimit-tier: - free x-rpc-node: - - lon68 + - fra113 status: code: 200 message: OK diff --git a/blockapi/test/v2/api/cassettes/test_parse_metaplex_account.yaml b/blockapi/test/v2/api/cassettes/test_parse_metaplex_account.yaml index 151ddffc..8a97a122 100644 --- a/blockapi/test/v2/api/cassettes/test_parse_metaplex_account.yaml +++ b/blockapi/test/v2/api/cassettes/test_parse_metaplex_account.yaml @@ -9,29 +9,28 @@ interactions: Connection: - keep-alive User-Agent: - - python-requests/2.31.0 + - python-requests/2.32.5 method: GET uri: https://gateway.pinata.cloud/ipfs/QmVfMPCUPs5V1omaDvoni38w8aEJpFTjEZRNRmoVyexXWe response: body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//RI9Ba8JAFITv/oohZzVYLAVvWjwI1dqq0FJ6eJs8s6vJ7rr7Qkyl/70k - Hnp8wzfzZm4DILFUcTJDMs8EK8yw14z5CtvgvOasTYYdFNtKubLHnvd3yVRU9EYt4uMsTQsSbqgd - e2NJaJyVrs5T448xfat202qaLU71uXj61Ka66OP7QT34w3KqFtsPFdR6oR71z3WjDvN7fM4xC8aL - cfa/nYlwluGOEM04cgMf3IkzieCrL10wtoB2DcSBbUEFozGiuz2KW2dzEDJX5iCbI6fKYzJydjRB - HTmkFKOJQlbgKVBuimoIVQsogmBZGhfO3W++1FTGe02+CttonI3JDLffXhMquutrAADJ7vVlvrmP - ApL1cr1MBsB3T2aBSVzorYPfPwAAAP//AwAE4qjcjwEAAA== + string: "{\n \"name\": \"Act I : The AI Prophecy\",\n \"symbol\": \"ACT\",\n + \ \"image\": \"https://gateway.pinata.cloud/ipfs/QmS4m4cBjukg7YhimqhfRUb2pUE4bBPXbrbMBb5hzxNbUA\",\n + \ \"description\": \"Act I is one of the few projects exploring how to engage + with AI beyond a cold and damp 1-on-1 user/assistant paradigm, but as a network + of equals\",\n \"extensions\": {},\n \"tags\": [\n \"SOLANA\",\n \"MEME\"\n + \ ],\n \"creator\": {}\n}" headers: Access-Control-Allow-Origin: - '*' Age: - - '198388' + - '4776' CF-Cache-Status: - HIT CF-Ray: - - 8d688cb5c809cbf3-MAD + - 976409232f4f80e8-PRG Cache-Control: - - public, max-age=864000 + - public, max-age=29030400 Connection: - keep-alive Content-Encoding: @@ -39,21 +38,22 @@ interactions: Content-Type: - application/json Date: - - Tue, 22 Oct 2024 09:44:35 GMT + - Thu, 28 Aug 2025 13:08:13 GMT ETag: - W/"QmVfMPCUPs5V1omaDvoni38w8aEJpFTjEZRNRmoVyexXWe" Expires: - - Fri, 01 Nov 2024 09:44:35 GMT + - Thu, 30 Jul 2026 13:08:13 GMT Server: - cloudflare + Set-Cookie: + - _cfuvid=s8i55DXcFh0WBxtugckw0rwrB6ACUXr.ttgTn8ZQbvc-1756386493815-0.0.1.1-604800000; + path=/; domain=.pinata.cloud; HttpOnly; Secure; SameSite=None Strict-Transport-Security: - - max-age=15724800; includeSubDomains + - max-age=31536000; includeSubDomains Transfer-Encoding: - chunked Vary: - Accept-Encoding - Via: - - 1.1 3e64b4bceb49543044d7ca6510e86e3a.cloudfront.net (CloudFront) X-Robots-Tag: - noindex access-control-allow-headers: @@ -75,18 +75,10 @@ interactions: content-security-policy: - 'default-src ''self''; img-src * data: blob: ''unsafe-inline''; style-src * ''unsafe-inline''' - x-amz-cf-id: - - Q2h-wBzIZXZ7r_fJ0O1vRsDizW7pZVa6jf0Ig2nKKR60zWRFmKpIow== - x-amz-cf-pop: - - MAD56-P1 - x-cache: - - Hit from cloudfront x-ipfs-path: - /ipfs/QmVfMPCUPs5V1omaDvoni38w8aEJpFTjEZRNRmoVyexXWe x-ipfs-roots: - QmVfMPCUPs5V1omaDvoni38w8aEJpFTjEZRNRmoVyexXWe - x-request-id: - - 9d0047ad6bed38faa10f9dd5ae9de7b1 status: code: 200 message: OK diff --git a/blockapi/test/v2/api/conftest.py b/blockapi/test/v2/api/conftest.py index 6ded2599..c0b8831f 100644 --- a/blockapi/test/v2/api/conftest.py +++ b/blockapi/test/v2/api/conftest.py @@ -1,6 +1,8 @@ import json import os -from typing import List, Union +from typing import Union + +import pytest def read_json_file(file_name: str) -> Union[list, dict]: @@ -13,3 +15,10 @@ def read_file(file_name: str) -> str: json_path = os.path.abspath(os.path.join(os.path.dirname(__file__), file_name)) with open(json_path, encoding="utf-8") as f: return f.read() + + +@pytest.fixture(scope='session') +def vcr_config(): + return { + 'decode_compressed_response': True, + } diff --git a/blockapi/test/v2/api/test_solana.py b/blockapi/test/v2/api/test_solana.py index 05eb0c01..671421f0 100644 --- a/blockapi/test/v2/api/test_solana.py +++ b/blockapi/test/v2/api/test_solana.py @@ -549,7 +549,7 @@ def metaplex_content(): 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ==' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==' ) diff --git a/blockapi/v2/api/perpetual/perpetual.py b/blockapi/v2/api/perpetual/perpetual.py index 29bc943d..4d846bae 100644 --- a/blockapi/v2/api/perpetual/perpetual.py +++ b/blockapi/v2/api/perpetual/perpetual.py @@ -49,7 +49,7 @@ def perp_contract_address(contract_name: str) -> ChecksumAddress: raise ValueError('Invalid contract name.') contract_address = contract_map[contract_name] - return Web3.toChecksumAddress(contract_address) + return Web3.to_checksum_address(contract_address) @lru_cache() diff --git a/blockapi/v2/api/synthetix/synthetix.py b/blockapi/v2/api/synthetix/synthetix.py index 7d1a6554..6f8cd882 100644 --- a/blockapi/v2/api/synthetix/synthetix.py +++ b/blockapi/v2/api/synthetix/synthetix.py @@ -84,7 +84,7 @@ def snx_contract_address( # as last 42 characters try: contract_address = result.url[-42:] - return Web3.toChecksumAddress(contract_address) + return Web3.to_checksum_address(contract_address) except Exception: raise ValueError(f'Contract {contract_name} not found.') @@ -118,7 +118,7 @@ def snx_optimism_contract_address( table = BeautifulSoup(html_tab_raw, 'lxml') row = table.find('td', text=contract_name).parent - return Web3.toChecksumAddress(row.contents[5].text.strip()) + return Web3.to_checksum_address(row.contents[5].text.strip()) except Exception: raise ValueError(f'Contract {contract_name} not found.') @@ -341,7 +341,7 @@ def _get_synth_contract(self, symbol: str) -> str: if symbol == 'SNX': synth_addr = easy_call(snx_contract, 'proxy') else: - synth_contract_addr = Web3.toChecksumAddress( + synth_contract_addr = Web3.to_checksum_address( easy_call(snx_contract, 'synths', symbol.encode()) ) synth_contract = self.w3.eth.contract(synth_contract_addr, abi=erc20_abi) diff --git a/blockapi/v2/api/web3_utils.py b/blockapi/v2/api/web3_utils.py index 4553d7fc..296cf531 100644 --- a/blockapi/v2/api/web3_utils.py +++ b/blockapi/v2/api/web3_utils.py @@ -133,10 +133,10 @@ def to_checksum_address(func): @functools.wraps(func) def inner(self, address, *args, **kwargs): - return func(self, Web3.toChecksumAddress(address), *args, **kwargs) + return func(self, Web3.to_checksum_address(address), *args, **kwargs) return inner def ensure_checksum_address(address: Optional[str]) -> Optional[str]: - return Web3.toChecksumAddress(address) if address is not None else None + return Web3.to_checksum_address(address) if address is not None else None diff --git a/setup.py b/setup.py index 2de5e16f..6584d694 100644 --- a/setup.py +++ b/setup.py @@ -18,23 +18,20 @@ long_description_content_type='text/markdown', packages=PACKAGES, install_requires=[ - 'requests==2.32.4', - 'pytz>=2019.2', + 'requests>=2.28,<3.0', 'python-dateutil>=2.8.0', 'coinaddrng==1.1.1', - 'cfscrape>=2.0.8', - 'ethereum_input_decoder>=0.2.2', - 'web3>=5.2.2,<6.0.0', + 'web3>=5.2.2,<8.0.0', 'bs4>=0.0.1', 'lxml>=4.4.1', 'pydantic>=1.10.2', - 'marko==1.3.0', + 'marko>=1.3.0,<2.0.0', 'fake_useragent>=1.1.3', 'pytest', 'pytest-vcr', 'requests_mock>=1.9.3', - 'attrs>=17.4.0,<=22.1.0', - 'solders==0.22.0', + 'attrs>=17.4.0,<23.0.0', + 'solders>=0.22.0', ], url="https://github.com/crypkit/blockapi", )