aiochainscan 0.2.1__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.
- aiochainscan-0.2.1/.importlinter +40 -0
- aiochainscan-0.2.1/GRAPHQL_SUPPORT_PLAN.md +127 -0
- aiochainscan-0.2.1/MANIFEST.in +36 -0
- aiochainscan-0.2.1/PKG-INFO +738 -0
- aiochainscan-0.2.1/README.md +700 -0
- aiochainscan-0.2.1/aiochainscan/__init__.py +2818 -0
- aiochainscan-0.2.1/aiochainscan/adapters/__init__.py +6 -0
- aiochainscan-0.2.1/aiochainscan/adapters/aiohttp_client.py +70 -0
- aiochainscan-0.2.1/aiochainscan/adapters/aiohttp_graphql_client.py +48 -0
- aiochainscan-0.2.1/aiochainscan/adapters/blockscout_graphql_builder.py +267 -0
- aiochainscan-0.2.1/aiochainscan/adapters/endpoint_builder_urlbuilder.py +51 -0
- aiochainscan-0.2.1/aiochainscan/adapters/memory_cache.py +36 -0
- aiochainscan-0.2.1/aiochainscan/adapters/noop_telemetry.py +16 -0
- aiochainscan-0.2.1/aiochainscan/adapters/retry_exponential.py +31 -0
- aiochainscan-0.2.1/aiochainscan/adapters/simple_provider_federator.py +33 -0
- aiochainscan-0.2.1/aiochainscan/adapters/simple_rate_limiter.py +55 -0
- aiochainscan-0.2.1/aiochainscan/adapters/structlog_telemetry.py +45 -0
- aiochainscan-0.2.1/aiochainscan/aiochainscan_fastabi.pyi +14 -0
- aiochainscan-0.2.1/aiochainscan/capabilities.py +95 -0
- aiochainscan-0.2.1/aiochainscan/cli.py +259 -0
- aiochainscan-0.2.1/aiochainscan/client.py +123 -0
- aiochainscan-0.2.1/aiochainscan/common.py +209 -0
- aiochainscan-0.2.1/aiochainscan/config.py +593 -0
- aiochainscan-0.2.1/aiochainscan/core/__init__.py +8 -0
- aiochainscan-0.2.1/aiochainscan/core/client.py +264 -0
- aiochainscan-0.2.1/aiochainscan/core/endpoint.py +142 -0
- aiochainscan-0.2.1/aiochainscan/core/method.py +62 -0
- aiochainscan-0.2.1/aiochainscan/decode.py +522 -0
- aiochainscan-0.2.1/aiochainscan/domain/__init__.py +12 -0
- aiochainscan-0.2.1/aiochainscan/domain/dto.py +164 -0
- aiochainscan-0.2.1/aiochainscan/domain/models.py +77 -0
- aiochainscan-0.2.1/aiochainscan/exceptions.py +64 -0
- aiochainscan-0.2.1/aiochainscan/fastabi/Cargo.lock +4174 -0
- aiochainscan-0.2.1/aiochainscan/fastabi/Cargo.toml +31 -0
- aiochainscan-0.2.1/aiochainscan/fastabi/src/lib.rs +861 -0
- aiochainscan-0.2.1/aiochainscan/modules/account.py +305 -0
- aiochainscan-0.2.1/aiochainscan/modules/base.py +136 -0
- aiochainscan-0.2.1/aiochainscan/modules/block.py +258 -0
- aiochainscan-0.2.1/aiochainscan/modules/contract.py +240 -0
- aiochainscan-0.2.1/aiochainscan/modules/extra/links.py +20 -0
- aiochainscan-0.2.1/aiochainscan/modules/extra/utils.py +643 -0
- aiochainscan-0.2.1/aiochainscan/modules/gas_tracker.py +185 -0
- aiochainscan-0.2.1/aiochainscan/modules/logs.py +101 -0
- aiochainscan-0.2.1/aiochainscan/modules/proxy.py +343 -0
- aiochainscan-0.2.1/aiochainscan/modules/stats.py +422 -0
- aiochainscan-0.2.1/aiochainscan/modules/token.py +292 -0
- aiochainscan-0.2.1/aiochainscan/modules/transaction.py +95 -0
- aiochainscan-0.2.1/aiochainscan/network.py +216 -0
- aiochainscan-0.2.1/aiochainscan/ports/__init__.py +5 -0
- aiochainscan-0.2.1/aiochainscan/ports/cache.py +19 -0
- aiochainscan-0.2.1/aiochainscan/ports/endpoint_builder.py +26 -0
- aiochainscan-0.2.1/aiochainscan/ports/graphql_client.py +22 -0
- aiochainscan-0.2.1/aiochainscan/ports/graphql_query_builder.py +68 -0
- aiochainscan-0.2.1/aiochainscan/ports/http_client.py +27 -0
- aiochainscan-0.2.1/aiochainscan/ports/provider_federator.py +23 -0
- aiochainscan-0.2.1/aiochainscan/ports/rate_limiter.py +20 -0
- aiochainscan-0.2.1/aiochainscan/ports/telemetry.py +16 -0
- aiochainscan-0.2.1/aiochainscan/py.typed +0 -0
- aiochainscan-0.2.1/aiochainscan/scanners/__init__.py +90 -0
- aiochainscan-0.2.1/aiochainscan/scanners/_etherscan_like.py +163 -0
- aiochainscan-0.2.1/aiochainscan/scanners/base.py +172 -0
- aiochainscan-0.2.1/aiochainscan/scanners/basescan_v1.py +40 -0
- aiochainscan-0.2.1/aiochainscan/scanners/blockscout_v1.py +177 -0
- aiochainscan-0.2.1/aiochainscan/scanners/etherscan_v2.py +99 -0
- aiochainscan-0.2.1/aiochainscan/scanners/moralis_v1.py +188 -0
- aiochainscan-0.2.1/aiochainscan/scanners/routscan_v1.py +202 -0
- aiochainscan-0.2.1/aiochainscan/services/__init__.py +53 -0
- aiochainscan-0.2.1/aiochainscan/services/_executor.py +65 -0
- aiochainscan-0.2.1/aiochainscan/services/account.py +1350 -0
- aiochainscan-0.2.1/aiochainscan/services/block.py +286 -0
- aiochainscan-0.2.1/aiochainscan/services/constants.py +21 -0
- aiochainscan-0.2.1/aiochainscan/services/contract.py +397 -0
- aiochainscan-0.2.1/aiochainscan/services/fetch_all.py +873 -0
- aiochainscan-0.2.1/aiochainscan/services/gas.py +166 -0
- aiochainscan-0.2.1/aiochainscan/services/logs.py +469 -0
- aiochainscan-0.2.1/aiochainscan/services/pagination.py +30 -0
- aiochainscan-0.2.1/aiochainscan/services/paging_engine.py +496 -0
- aiochainscan-0.2.1/aiochainscan/services/proxy.py +707 -0
- aiochainscan-0.2.1/aiochainscan/services/stats.py +965 -0
- aiochainscan-0.2.1/aiochainscan/services/token.py +397 -0
- aiochainscan-0.2.1/aiochainscan/services/transaction.py +269 -0
- aiochainscan-0.2.1/aiochainscan/services/unified_fetch.py +377 -0
- aiochainscan-0.2.1/aiochainscan/url_builder.py +198 -0
- aiochainscan-0.2.1/aiochainscan/utils/__init__.py +7 -0
- aiochainscan-0.2.1/aiochainscan/utils/date.py +33 -0
- aiochainscan-0.2.1/aiochainscan.egg-info/SOURCES.txt +116 -0
- aiochainscan-0.2.1/instructions.md +1121 -0
- aiochainscan-0.2.1/pyproject.toml +244 -0
- aiochainscan-0.2.1/setup.cfg +4 -0
- aiochainscan-0.2.1/setup.py +28 -0
- aiochainscan-0.2.1/tests/test_account.py +312 -0
- aiochainscan-0.2.1/tests/test_block.py +312 -0
- aiochainscan-0.2.1/tests/test_blockscout_ethereum_flow.py +265 -0
- aiochainscan-0.2.1/tests/test_capabilities.py +118 -0
- aiochainscan-0.2.1/tests/test_cli.py +475 -0
- aiochainscan-0.2.1/tests/test_client.py +67 -0
- aiochainscan-0.2.1/tests/test_common.py +68 -0
- aiochainscan-0.2.1/tests/test_config.py +443 -0
- aiochainscan-0.2.1/tests/test_contract.py +240 -0
- aiochainscan-0.2.1/tests/test_decode.py +397 -0
- aiochainscan-0.2.1/tests/test_decode_fastabi.py +322 -0
- aiochainscan-0.2.1/tests/test_decode_online.py +96 -0
- aiochainscan-0.2.1/tests/test_exceptions.py +74 -0
- aiochainscan-0.2.1/tests/test_gas_tracker.py +208 -0
- aiochainscan-0.2.1/tests/test_integration.py +630 -0
- aiochainscan-0.2.1/tests/test_links.py +41 -0
- aiochainscan-0.2.1/tests/test_logs.py +259 -0
- aiochainscan-0.2.1/tests/test_network.py +256 -0
- aiochainscan-0.2.1/tests/test_network_retry.py +173 -0
- aiochainscan-0.2.1/tests/test_proxy.py +324 -0
- aiochainscan-0.2.1/tests/test_stats.py +327 -0
- aiochainscan-0.2.1/tests/test_token.py +318 -0
- aiochainscan-0.2.1/tests/test_transaction.py +92 -0
- aiochainscan-0.2.1/tests/test_unified_client.py +342 -0
- aiochainscan-0.2.1/tests/test_url_builder.py +197 -0
- aiochainscan-0.2.1/tests/test_utils.py +236 -0
- aiochainscan-0.2.1/tests/test_utils_date.py +65 -0
- aiochainscan-0.2.1/tests/test_utils_extended.py +114 -0
- aiochainscan-0.2.1/tests/test_utils_optimized.py +283 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Import Linter configuration for aiochainscan.
|
|
2
|
+
#
|
|
3
|
+
# Initial contracts are intentionally permissive to avoid breaking existing code.
|
|
4
|
+
# We will tighten them iteratively during the migration.
|
|
5
|
+
|
|
6
|
+
[importlinter]
|
|
7
|
+
root_package = aiochainscan
|
|
8
|
+
|
|
9
|
+
[importlinter:contract:1]
|
|
10
|
+
name = Domain must be isolated
|
|
11
|
+
type = forbidden
|
|
12
|
+
source_modules =
|
|
13
|
+
aiochainscan.domain
|
|
14
|
+
forbidden_modules =
|
|
15
|
+
aiochainscan.ports
|
|
16
|
+
aiochainscan.services
|
|
17
|
+
aiochainscan.adapters
|
|
18
|
+
aiochainscan.core
|
|
19
|
+
aiochainscan.modules
|
|
20
|
+
aiochainscan.scanners
|
|
21
|
+
|
|
22
|
+
[importlinter:contract:2]
|
|
23
|
+
name = Ports must not depend on upper layers
|
|
24
|
+
type = forbidden
|
|
25
|
+
source_modules =
|
|
26
|
+
aiochainscan.ports
|
|
27
|
+
forbidden_modules =
|
|
28
|
+
aiochainscan.services
|
|
29
|
+
aiochainscan.adapters
|
|
30
|
+
aiochainscan.core
|
|
31
|
+
aiochainscan.modules
|
|
32
|
+
aiochainscan.scanners
|
|
33
|
+
|
|
34
|
+
[importlinter:contract:3]
|
|
35
|
+
name = Services should not depend on adapters (initial)
|
|
36
|
+
type = forbidden
|
|
37
|
+
source_modules =
|
|
38
|
+
aiochainscan.services
|
|
39
|
+
forbidden_modules =
|
|
40
|
+
aiochainscan.adapters
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
## GraphQL Support Plan (Hexagonal Architecture Only)
|
|
2
|
+
|
|
3
|
+
### Goals
|
|
4
|
+
- Provide first-class support for providers that expose GraphQL endpoints instead of Etherscan-like REST.
|
|
5
|
+
- Keep public facades and service APIs stable; integrate via ports/adapters only.
|
|
6
|
+
- Maintain TDD: add failing tests first, implement in small increments.
|
|
7
|
+
|
|
8
|
+
### Non-goals (for this phase)
|
|
9
|
+
- No changes to the unified core (ChainscanClient/scanners) path.
|
|
10
|
+
- No replacement of existing REST flow; GraphQL is an additional transport/provider path.
|
|
11
|
+
|
|
12
|
+
### Design Overview
|
|
13
|
+
- Extend the hexagonal layer with GraphQL-specific ports/adapters and a thin provider selection layer (federator) while preserving existing services.
|
|
14
|
+
- Unify pagination semantics across REST (page/offset) and GraphQL (cursor) via a generic Page[T] domain type and conversion helpers.
|
|
15
|
+
- Normalize data and errors at the adapter boundary to existing DTOs/normalizers.
|
|
16
|
+
|
|
17
|
+
### New and Updated Components
|
|
18
|
+
|
|
19
|
+
1) Domain (new types)
|
|
20
|
+
- Page[T]: generic, typed container for paginated results with opaque cursor strings
|
|
21
|
+
- Unifies REST (page/offset) and GraphQL (cursor-based) pagination patterns
|
|
22
|
+
- Maintains backward compatibility - only new typed facades use Page[T]
|
|
23
|
+
|
|
24
|
+
2) Ports (new protocols)
|
|
25
|
+
- **GraphQLClient**: Execute GraphQL queries with lifecycle management (execute, close)
|
|
26
|
+
- **GraphQLQueryBuilder**: Transform logical methods (Method enum) into provider-specific GraphQL queries
|
|
27
|
+
- **ProviderFederator**: Choose between REST/GraphQL based on capabilities, health, and configuration
|
|
28
|
+
|
|
29
|
+
3) Adapters (new implementations)
|
|
30
|
+
- **AiohttpGraphQLClient**: GraphQL client using aiohttp with existing retry/rate-limit/telemetry integration
|
|
31
|
+
- **BlockscoutGraphQLBuilder**: Builds Blockscout GraphQL queries for core methods (logs, transactions, token transfers)
|
|
32
|
+
- **SimpleProviderFederator**: Chooses REST vs GraphQL based on capabilities.py and health status
|
|
33
|
+
- **Future**: BitqueryGraphQLBuilder, SubgraphGraphQLBuilder as needed
|
|
34
|
+
|
|
35
|
+
4) File structure (new components only)
|
|
36
|
+
- **domain/models.py**: Add Page[T] generic type
|
|
37
|
+
- **ports/**: Add 3 new protocol files (graphql_client, graphql_query_builder, federator)
|
|
38
|
+
- **adapters/**: Add 3 new implementations (aiohttp_graphql_client, blockscout_graphql_builder, simple_provider_federator)
|
|
39
|
+
- **services/**: Edit existing files to add optional GraphQL DI parameters; add pagination.py for cursor helpers
|
|
40
|
+
- **capabilities.py**: Extend with GraphQL support flags per provider/network
|
|
41
|
+
|
|
42
|
+
5) Services (updates only)
|
|
43
|
+
- Inject optional GraphQL ports (GraphQLClient, GraphQLQueryBuilder) and ProviderFederator via DI.
|
|
44
|
+
- For methods where GraphQL is supported and either configured as preferred or REST is unavailable/429/5xx, execute via GraphQL.
|
|
45
|
+
- Keep the facade/service signatures unchanged; return existing DTOs/normalized dicts/typed DTOs.
|
|
46
|
+
- Add pagination parameter to list-returning services for GraphQL cursor support.
|
|
47
|
+
|
|
48
|
+
### Pagination Unification Strategy
|
|
49
|
+
- **Problem**: REST uses page/offset, GraphQL uses cursor-based pagination
|
|
50
|
+
- **Solution**: Page[T] domain type with opaque cursor strings that work for both
|
|
51
|
+
- **Implementation**:
|
|
52
|
+
- REST: encode page/offset as opaque string (`"page=2&offset=100"`)
|
|
53
|
+
- GraphQL: use native endCursor from response
|
|
54
|
+
- Conversion helpers in services/pagination.py
|
|
55
|
+
- **Backward compatibility**: Only new `*_typed` facades return Page[T], existing facades unchanged
|
|
56
|
+
|
|
57
|
+
### Error Handling Strategy
|
|
58
|
+
- **Problem**: GraphQL errors differ from HTTP errors (errors[] array vs status codes)
|
|
59
|
+
- **Solution**: Map GraphQL errors to existing Chainscan exception hierarchy
|
|
60
|
+
- **Implementation**: Handle both HTTP-level (transport) and GraphQL-level (query) errors in GraphQL adapter
|
|
61
|
+
- **Telemetry**: Add `provider_type` field to distinguish graphql vs rest errors
|
|
62
|
+
|
|
63
|
+
### Data Normalization Strategy
|
|
64
|
+
- **Reuse existing normalizers**: EIP-55 addresses, timestamps, hex conversions, wei amounts
|
|
65
|
+
- **GraphQL-specific mappers**: Transform GraphQL field names to match existing DTO structures
|
|
66
|
+
- **Consistency**: Same normalized output regardless of REST or GraphQL source
|
|
67
|
+
|
|
68
|
+
### Infrastructure Reuse
|
|
69
|
+
- **Ports**: Reuse existing RateLimiter, RetryPolicy, Cache, Telemetry ports
|
|
70
|
+
- **Composition**: Same DI pattern for GraphQL as REST (inject ports into adapters)
|
|
71
|
+
- **Telemetry**: Same event format with additional `provider_type` field
|
|
72
|
+
|
|
73
|
+
### Capability Management
|
|
74
|
+
- **Extension**: Add GraphQL support flags to capabilities.py per provider/network
|
|
75
|
+
- **Decision logic**: Federator uses capabilities to choose REST vs GraphQL
|
|
76
|
+
- **Examples**: `supports_logs_gql`, `supports_txlist_gql` for Blockscout networks
|
|
77
|
+
|
|
78
|
+
### Testing Strategy (TDD approach)
|
|
79
|
+
1. **Domain layer**: Pagination helpers (encode/decode cursors, REST↔GraphQL bridge)
|
|
80
|
+
2. **Adapter layer**: GraphQL client (error mapping, lifecycle), Query builders (query generation)
|
|
81
|
+
3. **Service layer**: DI integration, provider selection logic, normalization consistency
|
|
82
|
+
4. **Federator**: Capability-based selection, health tracking, fallback logic
|
|
83
|
+
5. **Integration**: End-to-end GraphQL→DTO flow
|
|
84
|
+
6. **Regression**: Existing REST tests remain green (no breaking changes)
|
|
85
|
+
|
|
86
|
+
### Implementation Roadmap
|
|
87
|
+
1. **Foundation**: Domain types (Page[T]), pagination helpers, ports protocols — DONE
|
|
88
|
+
2. **GraphQL adapter**: HTTP client with lifecycle, error mapping, telemetry integration — DONE
|
|
89
|
+
3. **Query builders**: Provider-specific generation for Blockscout (tx by hash, token transfers, address txs) — DONE (logs pending provider support)
|
|
90
|
+
4. **Service integration**: Optional GraphQL DI + fallback on REST — DONE (logs fallback preferred)
|
|
91
|
+
5. **Federator**: Capability-based selection + basic health hooks — DONE
|
|
92
|
+
6. **Capabilities**: Flags per provider/network (`*_gql`) — DONE
|
|
93
|
+
7. **Typed facades**: `get_logs_page_typed`, `get_token_transfers_page_typed`, `get_address_transactions_page_typed` — DONE
|
|
94
|
+
8. **Documentation**: Update usage/notes — PARTIAL (this plan updated)
|
|
95
|
+
|
|
96
|
+
### Usage Concepts (post-implementation)
|
|
97
|
+
|
|
98
|
+
#### Typed facades with GraphQL
|
|
99
|
+
- New `*_typed` facades return `Page[DTO]` with cursor-based pagination
|
|
100
|
+
- DI allows injecting GraphQL client/builder for preferred providers
|
|
101
|
+
- Federator automatically selects GraphQL when available and faster
|
|
102
|
+
|
|
103
|
+
#### Backward compatibility guarantee
|
|
104
|
+
- Existing untyped facades return `list[dict]` unchanged
|
|
105
|
+
- Same function signatures, same return formats
|
|
106
|
+
- GraphQL is opt-in via DI or federator selection, never forced
|
|
107
|
+
|
|
108
|
+
### Quality Assurance
|
|
109
|
+
- **Tests**: TDD approach, existing suite unchanged, comprehensive coverage
|
|
110
|
+
- **Code quality**: ruff, mypy --strict, import-linter contracts updated
|
|
111
|
+
- **Architecture**: Services don't import adapters, ports remain protocol-only
|
|
112
|
+
|
|
113
|
+
### Risk Analysis
|
|
114
|
+
- **Integration complexity**: Mitigate via DI and capability gating, no forced behavior changes
|
|
115
|
+
- **Pagination compatibility**: Opaque cursors handle both REST and GraphQL patterns
|
|
116
|
+
- **Error handling**: Centralized GraphQL error mapping in adapter layer
|
|
117
|
+
- **Performance**: Reuse HTTP sessions, apply existing retry/rate-limit patterns
|
|
118
|
+
|
|
119
|
+
### Decision Points
|
|
120
|
+
- **Provider priority**: Start with Blockscout (public, stable GraphQL)
|
|
121
|
+
- **Configuration**: Support both DI and environment-based GraphQL preference
|
|
122
|
+
- **Scope**: Focus on high-value endpoints (logs, transactions) in phase 1
|
|
123
|
+
|
|
124
|
+
### Rollout Strategy
|
|
125
|
+
- **Phase 1**: GraphQL for transaction by hash — DONE; logs kept on REST due to schema variance
|
|
126
|
+
- **Phase 1.1**: Token transfers, address transactions via GraphQL — DONE
|
|
127
|
+
- **Phase 1.2**: Federator health tracking — DONE; expand provider matrix — NEXT; docs sweep — NEXT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# MANIFEST.in - Specifies files to include in source distribution
|
|
2
|
+
|
|
3
|
+
# Documentation
|
|
4
|
+
include README.md
|
|
5
|
+
include LICENSE
|
|
6
|
+
include instructions.md
|
|
7
|
+
include GRAPHQL_SUPPORT_PLAN.md
|
|
8
|
+
|
|
9
|
+
# Configuration
|
|
10
|
+
include pyproject.toml
|
|
11
|
+
include setup.py
|
|
12
|
+
include .importlinter
|
|
13
|
+
|
|
14
|
+
# Type hints
|
|
15
|
+
include aiochainscan/py.typed
|
|
16
|
+
recursive-include aiochainscan *.pyi
|
|
17
|
+
|
|
18
|
+
# Rust extension source (for optional fast ABI decoder)
|
|
19
|
+
recursive-include aiochainscan/fastabi *.rs
|
|
20
|
+
include aiochainscan/fastabi/Cargo.toml
|
|
21
|
+
include aiochainscan/fastabi/Cargo.lock
|
|
22
|
+
|
|
23
|
+
# Exclude compiled/generated files
|
|
24
|
+
global-exclude *.pyc
|
|
25
|
+
global-exclude *.pyo
|
|
26
|
+
global-exclude __pycache__
|
|
27
|
+
global-exclude .DS_Store
|
|
28
|
+
global-exclude *.so
|
|
29
|
+
global-exclude *.dylib
|
|
30
|
+
|
|
31
|
+
# Exclude build artifacts
|
|
32
|
+
prune aiochainscan/fastabi/target
|
|
33
|
+
prune .venv
|
|
34
|
+
prune dist
|
|
35
|
+
prune build
|
|
36
|
+
prune *.egg-info
|