For Agents

Phase 2A of contract-scanner — SHIPPED 2026-05-30 (commit 536e318). Solves THE bottleneck the MVP exposed: DeFiLlama’s API exposes no contract address for small protocols, so the MVP could only scan a contract you fed it by hand (--address). Phase 2A resolves a protocol’s BSC contract addresses automatically from its DeFiLlama TVL adapter source on GitHub, so --slug (and --batch) work with zero manual address entry or web search. This note covers the resolution architecture, the empirical validation lessons (it auto-resolves ~60%, NOT ~100%), the robustness bugs fixed before building, and the deferred Phase 2.1 levers.

The bottleneck Phase 2A solves

The MVP (see contract-scanner) assumed DeFiLlama’s /protocol/{slug} endpoint would expose contract addresses for ~70% of small protocols. It doesn’t — for small protocols like EA Finance it returned [] (no addresses). The MVP could therefore only analyze a contract you supplied via --address. Phase 2A removes that manual step.

How adapter-anchored resolution works

Each DeFiLlama protocol record has a module field naming its TVL adapter — the JS/TS file in the DefiLlama/DefiLlama-Adapters GitHub repo that computes the protocol’s TVL by reading on-chain balances. That adapter contains the protocol’s contract addresses (it has to, to query them). Phase 2A mines them.

Resolution Pipeline

graph TD
    A["DeFiLlama protocol record<br/><i>module field</i>"] --> B["Fetch adapter source<br/>raw.githubusercontent.com/.../projects/&lt;module&gt;"]
    B -->|"404 (~36%)"| Z["needs-manual-address.csv<br/><i>no silent gap</i>"]
    B --> C["Regex-extract 0x addresses"]
    C --> D["Verify each on BSCScan<br/><i>Etherscan V2 chainid=56</i><br/>= confidence gate"]
    D --> E["Drop infra denylist<br/><i>Pancake router / WBNB /<br/>stables / CAKE</i>"]
    E --> F["Prefer Tier-1 over Tier-2<br/>+ cap per protocol"]
    F --> G["Resolved addresses<br/><i>carry resolution_source</i>"]
    G --> H["Stage 3 fetch → Slither"]
    style A fill:#264653,stroke:#2a9d8f,color:#fff
    style B fill:#264653,stroke:#2a9d8f,color:#fff
    style D fill:#3d2020,stroke:#c44,color:#fff
    style E fill:#2d2d2d,stroke:#888,color:#fff
    style Z fill:#3d2020,stroke:#c44,color:#fff
    style G fill:#1a4d2e,stroke:#4caf50,color:#fff

Steps

  1. Read module from the DeFiLlama protocol record.
  2. Fetch adapter source from raw.githubusercontent.com/.../projects/<module> (the DefiLlama-Adapters repo).
  3. Regex-extract 0x… addresses from the adapter source.
  4. Verify each address on BSCScan (Etherscan V2, chainid=56). Verification is the confidence gate — only addresses that resolve to a verified contract on BSC proceed. This filters out addresses for other chains that appear in multi-chain adapters.
  5. Drop infra denylist — known shared infrastructure that is NOT the protocol’s own code: PancakeSwap router, WBNB, common stablecoins, CAKE.
  6. Tier preference + cap. Prefer Tier-1 (address came from DeFiLlama’s own record) over Tier-2 (address mined from the adapter). Cap the number of addresses analyzed per protocol so one adapter can’t blow up the batch.
  7. Findings carry a resolution_source column (and contract_name) so a human reviewer can see how each address was obtained and judge attribution.
  8. Unresolved protocols are not dropped silently — they go to needs-manual-address.csv (a review bucket), so coverage gaps are visible, not hidden.

Empirical validation lessons (~25-protocol sample)

Adapter resolution auto-resolves ~60%, NOT ~100%

The EA Finance example (clean, single-chain, valid adapter) was best-case. Across a ~25-protocol sample the adapter approach auto-resolves roughly 60%. The remaining 40% need the deferred Phase 2.1 levers below. Do not assume full coverage.

  • ~36% of adapters 404. DeFiLlama keeps stale module pointers after the underlying file is deleted/renamed in the GitHub repo — the record still names a module that no longer exists. This is the single biggest coverage hole and the highest-value next lever (see Phase 2.1).
  • Multi-chain adapters mix addresses across chains. An adapter for a multi-chain protocol contains addresses for Ethereum, Polygon, etc., not just BSC. BSCScan verification filters most of these out (an Ethereum address won’t verify as a contract on BSC). Example: Frax — only 2 of 19 extracted addresses verify on BSC.
  • 3rd-party infra verifies on BSC but isn’t the protocol’s code. Routers, token contracts, and factory contracts (e.g. ICHIVaultFactory for TheDeep) verify on BSC but belong to shared infrastructure, not the protocol under review. The denylist + per-protocol cap + resolution_source/contract_name transparency mitigate this. Full own-code attribution is deferred — Phase 2A accepts some infra noise in exchange for coverage, and surfaces it transparently rather than hiding it.

The spec flaw this exposed: verified ≠ protocol's-own-code

BSCScan verification proves an address is a real verified contract on BSC — it does NOT prove the contract is the protocol’s own code. A verified PancakeSwap router is verified but irrelevant. This distinction (caught in pre-implementation validation, see below) is why the denylist, cap, and resolution_source transparency exist. Attribution to own-code is an unsolved, deferred problem.

Robustness bugs fixed (Part-0 hardening)

A 3-agent pre-implementation validation (see Process learning) found these real shipped bugs in the MVP before Phase 2A was built; they were fixed as Part-0 hardening:

Four silent-failure bugs in the MVP, fixed before Phase 2A

  • detect_language misclassified Solidity as Vyper on a substring match → the contract was silently skipped (Vyper isn’t Slither-analyzed the same way). Classic silent false-negative.
  • Malformed DeFiLlama records crashed the whole batch — one bad record took down the entire run instead of being skipped.
  • run_slice lacked exception isolation — an error in one protocol’s slice aborted the batch instead of being contained to that protocol.
  • Proxy implementation-fetch raise discarded recoverable analysis — when following a proxy to its implementation failed, the code raised and threw away analysis that could still proceed.

Live results (Phase 2A, real run)

What actually shipped works end-to-end with no manual input

  • --slug ea-finance (NO --address, no web search) now auto-resolves the wCC vault + StakingRewardsWCC via the adapter → 16 findings.
  • --batch --limit 8116 findings across 7 protocols + 1 correctly-reported unresolved (OpenGDP, a karak module mismatch → went to needs-manual-address.csv). No denylisted infra in the findings, no crash.

Deferred — Phase 2.1 (next levers, in rough priority)

Highest-value first

  1. Fallback for the ~36% of 404 adapters — the biggest coverage lever; lifting this is what gets resolution past ~60%.
  2. Chain-aware adapter parsing — parse which chain each address belongs to inside multi-chain adapters, instead of relying on BSCScan-verify to filter.
  3. Web / deployer-based resolution — resolve addresses from the project site or deployer EOA when the adapter path fails.
  4. A “fetch-failed” review bucket — separate fetch failures from genuine no-address cases.
  5. Multi-chain — extend beyond BSC (Stage 3 is already chainid-parameterized).
  6. Aderyn second-opinion — Cyfrin’s Rust analyzer for findings confirmed by both tools (also a contract-scanner Phase 2 item).

Process learning

Pre-implementation 3-agent validation caught a spec flaw AND real bugs before building — high-value pattern

Before implementing Phase 2A, a 3-agent validation was run:

  1. Spec audit — caught the fundamental spec flaw: verified ≠ protocol’s-own-code (BSCScan verification is a confidence gate, not an attribution proof). This reshaped the design (denylist, cap, resolution_source).
  2. Empirical funnel test — measured the real adapter-resolution rate (~60%, not ~100%) and the 404 rate (~36%) on a live sample before committing to the approach.
  3. MVP re-audit — found the four Part-0 silent-failure bugs above in already-shipped MVP code.

Lesson: spending three read-only agents to audit spec + empirically test the funnel + re-audit existing code before writing the implementation caught a design-level flaw and real bugs cheaply. Worth repeating for the next phase.

Specs / plans on disk

  • Design: /Users/levander/coding/bsc-vuln-scanner/docs/superpowers/specs/2026-05-30-phase2-address-resolution-design.md
  • Plan: /Users/levander/coding/bsc-vuln-scanner/docs/superpowers/plans/2026-05-30-phase2-address-resolution.md