Skip to content

Conversation

@VaitaR
Copy link
Owner

@VaitaR VaitaR commented Oct 9, 2025

🎯 Changes Summary

This PR removes the deprecated BaseScan scanner and integrates Base network support through Etherscan V2 API.

✅ What was done:

  1. Removed BaseScanV1 scanner - No longer needed as separate implementation
  2. Integrated Base via Etherscan V2 - Uses chain_id 8453 for Base mainnet
  3. Updated UrlBuilder - Base network now uses etherscan.io domain
  4. Fixed EtherscanV2 auth - Changed from header to query parameter auth
  5. Updated configuration - Base network works through Etherscan V2
  6. Updated tests - Reflects new Base network support
  7. Updated documentation - Shows Base via Etherscan V2

🚀 Benefits:

  • Unified interface - Same API for all EVM networks
  • Reduced complexity - One less scanner to maintain
  • Better integration - Uses official Etherscan V2 support for Base
  • Consistent behavior - All networks use same authentication method

📋 Technical Details:

  • Base network: ChainscanClient.from_config('etherscan', 'v2', 'base', 'main')
  • Authentication: ETHERSCAN_KEY (same as other Etherscan V2 networks)
  • Chain ID: 8453 (Base mainnet)
  • URL: https://api.etherscan.io/v2/api?chainid=8453

🧪 Testing:

  • ✅ Base client creation works
  • ✅ Balance queries work correctly
  • ✅ All existing tests pass (except external API issues)
  • ✅ Configuration properly detects Base as Etherscan V2 network

🔄 Migration:

Existing code using BaseScan will need to be updated to use Etherscan V2:

# Before (deprecated)
client = ChainscanClient.from_config('basescan', 'v1', 'base', 'main')

# After (recommended)
client = ChainscanClient.from_config('etherscan', 'v2', 'base', 'main')

VaitaR added 2 commits October 9, 2025 09:17
- Remove BaseScanV1 scanner implementation
- Integrate Base network support via Etherscan V2 with chain_id 8453
- Update UrlBuilder to use etherscan.io for Base network
- Update configuration and tests to reflect Base via Etherscan V2
- Fix EtherscanV2 authentication to use query parameter instead of header
- Update documentation to show Base network via Etherscan V2
@VaitaR VaitaR merged commit 316ca04 into main Oct 9, 2025
12 checks passed
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting

Comment on lines +74 to +161
@pytest.mark.asyncio
async def test_blockscout_two_chains_live() -> None:
# BlockScout typically doesn't require API keys
tests = [
('blockscout', 'v1', 'blockscout_eth', 'eth'),
('blockscout', 'v1', 'blockscout_polygon', 'polygon'),
]

for scanner_name, version, scanner_id, network in tests:
client = ChainscanClient.from_config(scanner_name, version, scanner_id, network)
await _assert_balance_ok(client, TEST_ADDRESS)
await client.close()
# Gentle pacing between providers
await asyncio.sleep(0.2)


@pytest.mark.asyncio
async def test_etherscan_two_chains_live() -> None:
# Requires ETHERSCAN_KEY in env
etherscan_key = os.getenv('ETHERSCAN_KEY')
if not etherscan_key or 'YOUR_' in etherscan_key or len(etherscan_key) < 10:
pytest.skip('ETHERSCAN_KEY not configured for live Etherscan tests')

tests = [
('etherscan', 'v2', 'eth', 'main'),
('etherscan', 'v2', 'arbitrum', 'main'),
('etherscan', 'v2', 'base', 'main'), # Base network via Etherscan V2
]

for scanner_name, version, scanner_id, network in tests:
# Etherscan V2 scanners use ETHERSCAN_KEY
if scanner_id in ('eth', 'arbitrum', 'bsc', 'polygon', 'optimism', 'base'):
if not _has_etherscan_key():
pytest.skip(f'ETHERSCAN_KEY not configured for {scanner_id}')
elif not _has_api_key(scanner_id):
pytest.skip(f'Missing API key for {scanner_id}')

client = ChainscanClient.from_config(scanner_name, version, scanner_id, network)
try:
await _assert_balance_ok(client, TEST_ADDRESS)
except ChainscanClientApiError as e: # pragma: no cover - live guardrail
# Gracefully skip if the environment key is invalid/rate-limited
msg = str(e)
if (
'Invalid API Key' in msg
or 'Missing/Invalid API Key' in msg
or 'rate limit' in msg.lower()
):
pytest.skip(f'Etherscan live test skipped due to API key/limits: {msg}')
raise
finally:
await client.close()
await asyncio.sleep(0.2)


# Base network is now supported through Etherscan V2 with chain_id
# No need for separate BaseScan scanner


@pytest.mark.asyncio
async def test_moralis_two_chains_live() -> None:
# Moralis requires MORALIS_API_KEY
tests = [
('moralis', 'v1', 'moralis', 'eth'),
('moralis', 'v1', 'moralis', 'arbitrum'),
]

for scanner_name, version, scanner_id, network in tests:
if not _has_api_key(scanner_id):
pytest.skip('Missing MORALIS_API_KEY')
client = ChainscanClient.from_config(scanner_name, version, scanner_id, network)
await _assert_balance_ok(client, TEST_ADDRESS)
await client.close()
await asyncio.sleep(0.2)


@pytest.mark.asyncio
async def test_routscan_mode_live() -> None:
# RoutScan supports Mode only (one network)
# RoutScan may not be registered in config in all environments; skip if unknown
try:
scanner_name, version, scanner_id, network = ('routscan', 'v1', 'routscan_mode', 'mode')
client = ChainscanClient.from_config(scanner_name, version, scanner_id, network)
except Exception as e:
pytest.skip(f'RoutScan not available in this build: {e}')
# Address may be zero on Mode; we still validate shape
await _assert_balance_ok(client, TEST_ADDRESS)
await client.close()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard live balance tests behind opt‑in flag

The new tests/test_e2e_balances_live.py runs real HTTP calls to BlockScout, Moralis, RoutScan, and Etherscan every time pytest is executed. Aside from skipping when API keys are missing, these tests are always enabled and perform network I/O (ChainscanClient.from_config(...); await _assert_balance_ok(...)). In environments without Internet access or when rate limits are hit, the default test suite will now fail rather than being skipped. Consider gating these tests behind an explicit marker or environment flag so the regular unit test run remains deterministic and offline-friendly.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants