Static-analysis utility for subgraph mappings (entity overwrite, null safety, division guards, and more).
License: MIT
Handlers often load an entity (for example GraphNetwork.load('1')), call helpers that reload the same entity and persist counter updates, then save the original instance without reloading. The last save() wipes whatever the helper stored. This detector walks every handler referenced by your manifest(s), inlines helper call graphs, and currently runs multiple checks:
[entity-overwrite]Flags “load → helper mutates/save → handler saves stale copy” patterns.[unexpected-null]Ensures newly constructed entities initialize every non-null, non-@derivedFromfield before calling.save()and warns when code writes to derived fields.[unchecked-load]Warns whenEntity.load(...)results are forced non-null with!instead of handling the absent entity case explicitly.[unchecked-nonnull]Fails whengraphNetwork.currentL1BlockNumber!is used instead of checking that the optional field is set on L2 code paths.[division-guard]Errors when code divides by a value that has not been proven non-zero (e.g.,x / shareswithout checkingshares !== 0). The check understands common Graph helpers such asif (!shares.isZero()) { ... },if (shares.gt(BigInt.fromI32(0))) { ... }, and ternaries that short-circuit onBigDecimal.fromString(value.toString()), so legitimate guards don't raise noise.[derived-field-guard]Warns when handlers mutate metrics that feed helper routines (e.g.,updateDelegationExchangeRate) but skip calling the helper before saving, so you know to recompute derived state before persisting.[helper-return-contract]Warns at helper call sites when the helper may return an entity with required fields still unset, nudging the caller to initialize those fields before the next.save().
cd subgraph-linter
npm install
npm run build
# Analyze the handlers referenced by a manifest (tsconfig inferred)
npm run check -- --manifest ../graph-network-analytics-horizon/subgraph.yaml
# Provide an explicit tsconfig when the default isn't correct
npm run check -- --manifest ../graph-network-analytics-horizon/subgraph.yaml --tsconfig ../graph-network-analytics-horizon/tsconfig.json
# Override the analyzer config (defaults to subgraph-linter.config.json when present)
npm run check -- --manifest ../graph-network-analytics-horizon/subgraph.yaml --config ./detector.config.json- At least one
--manifest <path>flag is required. You can repeat the flag to merge multiple manifests. - The detector inspects each manifest’s
mapping.fileentries to know which TypeScript files contain the handlers, and only analyzes the functions referenced by the manifest event/call/block/full handler lists. --tsconfig <path>is optional; when omitted we default to the repository’stsconfig.json.--config <path>is optional; when omitted we look forsubgraph-linter.config.jsonin the current directory. The config file currently supportsseverityOverrides, which remaps check ids to'warning'or'error':
Results are grouped into Errors and Warnings; only issues whose effective severity is error (after overrides) cause a non‑zero exit code. Each line follows file:line - handler [checkId]: message so you can diff outputs or feed them into other tools.
A lightweight mock subgraph lives under test-fixtures/ with deterministic positives/negatives so we can regression-test the analyzer. To run it:
npm run build
npm run check:fixtures
# Enforce that fixture positives match the expected list
npm run verify:fixturesThe script wires in test-fixtures/subgraph.yaml, so only the handlers enumerated there (including the manifest-only processAllocation) are analyzed. You should see the expected positives in test-fixtures/src/mappings/positives.ts (both the overwrite cases and the unexpected-null cases, including the derived-field violation); negatives demonstrate safe patterns such as explicit reloads or distinct entity ids. npm run verify:fixtures runs the analyzer and compares the diagnostic list against test-fixtures/expected-issues.json, failing fast if anything changes.
- Implementation is in
src/index.tsusingts-morph. - The detector tracks entity identity (type + id expression), assignment aliases, helper return values, and parameter mutations.
- When adding new heuristics, update the fixtures and re-run both
npm run check:fixturesand the real subgraph check to ensure we’re not reintroducing noise. - Need to add another static-analysis rule? See
ADDING_CHECKS.mdfor the step-by-step guide (implementation, fixtures, expected issues, and documentation).
Feel free to extend the fixtures with additional corner cases as we encounter new patterns in production subgraphs.
{ "severityOverrides": { "helper-return-contract": "warning", "division-guard": "error" } }