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.
Files changed (119) hide show
  1. aiochainscan-0.2.1/.importlinter +40 -0
  2. aiochainscan-0.2.1/GRAPHQL_SUPPORT_PLAN.md +127 -0
  3. aiochainscan-0.2.1/MANIFEST.in +36 -0
  4. aiochainscan-0.2.1/PKG-INFO +738 -0
  5. aiochainscan-0.2.1/README.md +700 -0
  6. aiochainscan-0.2.1/aiochainscan/__init__.py +2818 -0
  7. aiochainscan-0.2.1/aiochainscan/adapters/__init__.py +6 -0
  8. aiochainscan-0.2.1/aiochainscan/adapters/aiohttp_client.py +70 -0
  9. aiochainscan-0.2.1/aiochainscan/adapters/aiohttp_graphql_client.py +48 -0
  10. aiochainscan-0.2.1/aiochainscan/adapters/blockscout_graphql_builder.py +267 -0
  11. aiochainscan-0.2.1/aiochainscan/adapters/endpoint_builder_urlbuilder.py +51 -0
  12. aiochainscan-0.2.1/aiochainscan/adapters/memory_cache.py +36 -0
  13. aiochainscan-0.2.1/aiochainscan/adapters/noop_telemetry.py +16 -0
  14. aiochainscan-0.2.1/aiochainscan/adapters/retry_exponential.py +31 -0
  15. aiochainscan-0.2.1/aiochainscan/adapters/simple_provider_federator.py +33 -0
  16. aiochainscan-0.2.1/aiochainscan/adapters/simple_rate_limiter.py +55 -0
  17. aiochainscan-0.2.1/aiochainscan/adapters/structlog_telemetry.py +45 -0
  18. aiochainscan-0.2.1/aiochainscan/aiochainscan_fastabi.pyi +14 -0
  19. aiochainscan-0.2.1/aiochainscan/capabilities.py +95 -0
  20. aiochainscan-0.2.1/aiochainscan/cli.py +259 -0
  21. aiochainscan-0.2.1/aiochainscan/client.py +123 -0
  22. aiochainscan-0.2.1/aiochainscan/common.py +209 -0
  23. aiochainscan-0.2.1/aiochainscan/config.py +593 -0
  24. aiochainscan-0.2.1/aiochainscan/core/__init__.py +8 -0
  25. aiochainscan-0.2.1/aiochainscan/core/client.py +264 -0
  26. aiochainscan-0.2.1/aiochainscan/core/endpoint.py +142 -0
  27. aiochainscan-0.2.1/aiochainscan/core/method.py +62 -0
  28. aiochainscan-0.2.1/aiochainscan/decode.py +522 -0
  29. aiochainscan-0.2.1/aiochainscan/domain/__init__.py +12 -0
  30. aiochainscan-0.2.1/aiochainscan/domain/dto.py +164 -0
  31. aiochainscan-0.2.1/aiochainscan/domain/models.py +77 -0
  32. aiochainscan-0.2.1/aiochainscan/exceptions.py +64 -0
  33. aiochainscan-0.2.1/aiochainscan/fastabi/Cargo.lock +4174 -0
  34. aiochainscan-0.2.1/aiochainscan/fastabi/Cargo.toml +31 -0
  35. aiochainscan-0.2.1/aiochainscan/fastabi/src/lib.rs +861 -0
  36. aiochainscan-0.2.1/aiochainscan/modules/account.py +305 -0
  37. aiochainscan-0.2.1/aiochainscan/modules/base.py +136 -0
  38. aiochainscan-0.2.1/aiochainscan/modules/block.py +258 -0
  39. aiochainscan-0.2.1/aiochainscan/modules/contract.py +240 -0
  40. aiochainscan-0.2.1/aiochainscan/modules/extra/links.py +20 -0
  41. aiochainscan-0.2.1/aiochainscan/modules/extra/utils.py +643 -0
  42. aiochainscan-0.2.1/aiochainscan/modules/gas_tracker.py +185 -0
  43. aiochainscan-0.2.1/aiochainscan/modules/logs.py +101 -0
  44. aiochainscan-0.2.1/aiochainscan/modules/proxy.py +343 -0
  45. aiochainscan-0.2.1/aiochainscan/modules/stats.py +422 -0
  46. aiochainscan-0.2.1/aiochainscan/modules/token.py +292 -0
  47. aiochainscan-0.2.1/aiochainscan/modules/transaction.py +95 -0
  48. aiochainscan-0.2.1/aiochainscan/network.py +216 -0
  49. aiochainscan-0.2.1/aiochainscan/ports/__init__.py +5 -0
  50. aiochainscan-0.2.1/aiochainscan/ports/cache.py +19 -0
  51. aiochainscan-0.2.1/aiochainscan/ports/endpoint_builder.py +26 -0
  52. aiochainscan-0.2.1/aiochainscan/ports/graphql_client.py +22 -0
  53. aiochainscan-0.2.1/aiochainscan/ports/graphql_query_builder.py +68 -0
  54. aiochainscan-0.2.1/aiochainscan/ports/http_client.py +27 -0
  55. aiochainscan-0.2.1/aiochainscan/ports/provider_federator.py +23 -0
  56. aiochainscan-0.2.1/aiochainscan/ports/rate_limiter.py +20 -0
  57. aiochainscan-0.2.1/aiochainscan/ports/telemetry.py +16 -0
  58. aiochainscan-0.2.1/aiochainscan/py.typed +0 -0
  59. aiochainscan-0.2.1/aiochainscan/scanners/__init__.py +90 -0
  60. aiochainscan-0.2.1/aiochainscan/scanners/_etherscan_like.py +163 -0
  61. aiochainscan-0.2.1/aiochainscan/scanners/base.py +172 -0
  62. aiochainscan-0.2.1/aiochainscan/scanners/basescan_v1.py +40 -0
  63. aiochainscan-0.2.1/aiochainscan/scanners/blockscout_v1.py +177 -0
  64. aiochainscan-0.2.1/aiochainscan/scanners/etherscan_v2.py +99 -0
  65. aiochainscan-0.2.1/aiochainscan/scanners/moralis_v1.py +188 -0
  66. aiochainscan-0.2.1/aiochainscan/scanners/routscan_v1.py +202 -0
  67. aiochainscan-0.2.1/aiochainscan/services/__init__.py +53 -0
  68. aiochainscan-0.2.1/aiochainscan/services/_executor.py +65 -0
  69. aiochainscan-0.2.1/aiochainscan/services/account.py +1350 -0
  70. aiochainscan-0.2.1/aiochainscan/services/block.py +286 -0
  71. aiochainscan-0.2.1/aiochainscan/services/constants.py +21 -0
  72. aiochainscan-0.2.1/aiochainscan/services/contract.py +397 -0
  73. aiochainscan-0.2.1/aiochainscan/services/fetch_all.py +873 -0
  74. aiochainscan-0.2.1/aiochainscan/services/gas.py +166 -0
  75. aiochainscan-0.2.1/aiochainscan/services/logs.py +469 -0
  76. aiochainscan-0.2.1/aiochainscan/services/pagination.py +30 -0
  77. aiochainscan-0.2.1/aiochainscan/services/paging_engine.py +496 -0
  78. aiochainscan-0.2.1/aiochainscan/services/proxy.py +707 -0
  79. aiochainscan-0.2.1/aiochainscan/services/stats.py +965 -0
  80. aiochainscan-0.2.1/aiochainscan/services/token.py +397 -0
  81. aiochainscan-0.2.1/aiochainscan/services/transaction.py +269 -0
  82. aiochainscan-0.2.1/aiochainscan/services/unified_fetch.py +377 -0
  83. aiochainscan-0.2.1/aiochainscan/url_builder.py +198 -0
  84. aiochainscan-0.2.1/aiochainscan/utils/__init__.py +7 -0
  85. aiochainscan-0.2.1/aiochainscan/utils/date.py +33 -0
  86. aiochainscan-0.2.1/aiochainscan.egg-info/SOURCES.txt +116 -0
  87. aiochainscan-0.2.1/instructions.md +1121 -0
  88. aiochainscan-0.2.1/pyproject.toml +244 -0
  89. aiochainscan-0.2.1/setup.cfg +4 -0
  90. aiochainscan-0.2.1/setup.py +28 -0
  91. aiochainscan-0.2.1/tests/test_account.py +312 -0
  92. aiochainscan-0.2.1/tests/test_block.py +312 -0
  93. aiochainscan-0.2.1/tests/test_blockscout_ethereum_flow.py +265 -0
  94. aiochainscan-0.2.1/tests/test_capabilities.py +118 -0
  95. aiochainscan-0.2.1/tests/test_cli.py +475 -0
  96. aiochainscan-0.2.1/tests/test_client.py +67 -0
  97. aiochainscan-0.2.1/tests/test_common.py +68 -0
  98. aiochainscan-0.2.1/tests/test_config.py +443 -0
  99. aiochainscan-0.2.1/tests/test_contract.py +240 -0
  100. aiochainscan-0.2.1/tests/test_decode.py +397 -0
  101. aiochainscan-0.2.1/tests/test_decode_fastabi.py +322 -0
  102. aiochainscan-0.2.1/tests/test_decode_online.py +96 -0
  103. aiochainscan-0.2.1/tests/test_exceptions.py +74 -0
  104. aiochainscan-0.2.1/tests/test_gas_tracker.py +208 -0
  105. aiochainscan-0.2.1/tests/test_integration.py +630 -0
  106. aiochainscan-0.2.1/tests/test_links.py +41 -0
  107. aiochainscan-0.2.1/tests/test_logs.py +259 -0
  108. aiochainscan-0.2.1/tests/test_network.py +256 -0
  109. aiochainscan-0.2.1/tests/test_network_retry.py +173 -0
  110. aiochainscan-0.2.1/tests/test_proxy.py +324 -0
  111. aiochainscan-0.2.1/tests/test_stats.py +327 -0
  112. aiochainscan-0.2.1/tests/test_token.py +318 -0
  113. aiochainscan-0.2.1/tests/test_transaction.py +92 -0
  114. aiochainscan-0.2.1/tests/test_unified_client.py +342 -0
  115. aiochainscan-0.2.1/tests/test_url_builder.py +197 -0
  116. aiochainscan-0.2.1/tests/test_utils.py +236 -0
  117. aiochainscan-0.2.1/tests/test_utils_date.py +65 -0
  118. aiochainscan-0.2.1/tests/test_utils_extended.py +114 -0
  119. 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