Follow-up to #367 / #368, from the review in #368 (comment).
Where #368 leaves this
#368 makes storage coverage decide the verdict: any proven conflict is INCOMPATIBLE, anything the check can't see is UNKNOWN, and only full coverage is COMPATIBLE. To get there without a compiler it detects non-positional storage access and resolves roots in a few bounded forms:
- Scope is the inheritance chain plus library functions reachable by name. Receivers aren't resolved, so this over-approximates toward
UNKNOWN.
- Custom value types resolve by a declaration that is unique across the bundle. Ambiguous or missing declarations are gaps.
- Roots resolve from literals, unique constants, one local or getter hop, and the
keccak256, EIP-1967 and ERC-7201 expressions.
- A namespace is proven unchanged only by an identical elementary struct plus a verified root.
- Unannotated accessors, raw
sload/sstore and delegatecall are reported as gaps, not analysed.
The outcome is correct, but it is conservative. On the 3Jane upgrade, USD3's positional layout is COMPATIBLE while the combined verdict is UNKNOWN. This issue tracks turning the gaps that can be closed into real coverage.
Work
- Follow the
delegatecall target when it is a known verified contract. Yearn tokenized strategies delegatecall a shared TokenizedStrategy, which writes the proxy's storage at yearn.base.strategy.storage. Resolving that target and adding its code to the scope would turn the most common gap for Yearn strategies into checked coverage.
- Resolve the receivers of library calls instead of reaching by name. Today, using any function of
SafeERC20 or Address can pull an unrelated functionDelegateCall into scope.
- Follow storage references passed into helpers. This covers storage-pointer parameters and
using … for on storage types.
- Resolve custom value types with lexical scope and import aliases, rather than a bundle-unique name. That covers contract-scoped types (
C.Id) and names that are declared twice but used in only one scope.
- Compare namespace struct layouts with compiler-derived data, so compatible changes such as an appended member can be accepted. Today only text-identical elementary structs are proven.
- Evaluate root expressions more broadly through general constant folding, instead of the fixed forms listed above.
- Detect overlapping namespace ranges, i.e. a struct extent crossing another root. Today only exact duplicate roots are detected.
- Use compiler ASTs when a matching build supplies one. Sourcify returns no AST for these contracts (0 of 35 sources for the new USD3). The monitoring cron must still never download or run a compiler.
Acceptance
- Each item comes with a regression fixture that fails before its fix.
- No compiler download or execution, and no new network dependency in unit tests.
- Verdict precedence is unchanged. A proven conflict stays
INCOMPATIBLE even when another region is unknown, and incomplete coverage never produces an unqualified COMPATIBLE.
🤖 Generated with Claude Code
Follow-up to #367 / #368, from the review in #368 (comment).
Where #368 leaves this
#368 makes storage coverage decide the verdict: any proven conflict is
INCOMPATIBLE, anything the check can't see isUNKNOWN, and only full coverage isCOMPATIBLE. To get there without a compiler it detects non-positional storage access and resolves roots in a few bounded forms:UNKNOWN.keccak256, EIP-1967 and ERC-7201 expressions.sload/sstoreanddelegatecallare reported as gaps, not analysed.The outcome is correct, but it is conservative. On the 3Jane upgrade, USD3's positional layout is
COMPATIBLEwhile the combined verdict isUNKNOWN. This issue tracks turning the gaps that can be closed into real coverage.Work
delegatecalltarget when it is a known verified contract. Yearn tokenized strategies delegatecall a sharedTokenizedStrategy, which writes the proxy's storage atyearn.base.strategy.storage. Resolving that target and adding its code to the scope would turn the most common gap for Yearn strategies into checked coverage.SafeERC20orAddresscan pull an unrelatedfunctionDelegateCallinto scope.using … foron storage types.C.Id) and names that are declared twice but used in only one scope.Acceptance
INCOMPATIBLEeven when another region is unknown, and incomplete coverage never produces an unqualifiedCOMPATIBLE.🤖 Generated with Claude Code