You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reviewed the full diff plus utils/store.py, utils/logger.py, utils/alert.py, utils/telegram.py, protocols/yearn/alert_large_flows.py and check_indexer_freshness.py, and queried Kong live to check the real vault population.
Correctness
🔴 alert_small_parent_flows.py:293 — silently drops events after any gap longer than the lookback
since_ts = 0 if persisted_cursor else now - lookback, and save_cursor only ever runs inside the per-event loop. A chain/flow pair that returns zero events never stores a cursor, so it stays on the 2-hour lookback indefinitely — for as long as that pair has never seen a single qualifying event.
So any run gap longer than 2h permanently drops every event in [last_run, now-7200), with no error and nothing in the logs. Realistic causes: VPS down, a crash, or a run_with_alert abort on an earlier chain (see the next two findings, both of which abort the run).
Fix: persist a floor cursor/timestamp even on an empty page — e.g. store the chain's latest indexed block, or a last_scanned_ts — so resumption never depends on an event having been seen.
kong.py:200 — one malformed Kong row kills the whole monitor, permanently
fetch_kong_parent_vaults raises KongRequestError when any single vault is missing asset/decimals, and nothing catches it, so run_with_alert aborts the run before the remaining chains are scanned — every run, for as long as Kong serves that row. The sibling fetch_kong_vaults (~line 121) deliberately continues past bad rows instead.
Kong is clean today (0 of 117 parent vaults missing asset metadata), which is exactly why this would only ever fire at the worst time. Log and skip.
alert_small_parent_flows.py:342 — an Envio outage floods the channel
gql_request sends a send_envio_error_message on every failure and nothing short-circuits: monitor_flow_type breaks its page loop, monitor_chain tries the next flow type, main the next chain. With the indexer down, one run emits up to 12 near-identical Telegram messages.
alert_large_flows sends one per run, and check_indexer_freshness has an explicit 6-hour cooldown for exactly this. Abort the run (or set an "envio is down" flag) after the first failure.
alert_small_parent_flows.py:276 — unbounded alert fan-out, no aggregation or cap
Every qualifying event is its own immediate send_alert → send_telegram_message; no dedupe, batching or rate limiting anywhere in that path. A page can hold 1000 events and the loop keeps paging.
On yvUSDC a "small" flow is a 0.01 USDC deposit — trivially spammable — so a single run can try to push hundreds of messages into a group Telegram rate-limits at ~20/min, wedging the run and burying every other yearn alert. Aggregate a run's hits into one message with a per-run cap and an "N more" tail.
Coverage gaps: two sets of live vaults are never monitored
kong.py:194 — isHidden excludes live vaults with real TVL. Filtering on isHidden in addition to isRetired drops 9 vaults that are hidden-but-not-retired, including yETH-Recovery on mainnet (0xd7a540ba3626c0aa66e7DB4088971d0CD64695B6) with ~$6.0M TVL. isHidden is a UI-visibility flag, not a liveness flag; a $6M vault being dust-probed is precisely what this monitor exists to catch. Filter on isRetired only (optionally plus TVL > 0).
Berachain (80094) has 2 active parent vaults (yBERA, yHONEY) that go unmonitored because Chain has no member for it.
alert_small_parent_flows.py:33 — a single raw threshold means wildly different things per vault
10_000 raw units is 1e-14 WETH on an 18-decimal vault (a genuine dust probe), 0.01 USDC on a 6-decimal one, but 0.0001 WBTC ≈ $11 on yvWBTC-1 (8 decimals). So ordinary small WBTC deposits will page as "small parent-vault deposit" while the same dollar amount in yvUSDC never will. asset_decimals is already fetched and used for display — scale the threshold per vault, or express it in USD like alert_large_flows.
alert_small_parent_flows.py:158 — AttributeError on a "data": null response
response.get("data", {}).get("events") — the {} default only applies when the key is absent. GraphQL servers routinely return {"data": null, ...}; if that arrives without an errors key (already handled above), this raises AttributeError: 'NoneType' object has no attribute 'get' and aborts the whole run rather than skipping. Use (response.get("data") or {}).
alert_small_parent_flows.py:387 — --log-level / SMALL_PARENT_FLOWS_LOG_LEVEL are no-ops
logging.basicConfig configures the root logger, but utils.logger.get_logger (line 41) attaches its own handler and sets propagate = False, taking its level from the LOG_LEVEL env var. So --log-level=DEBUG changes no output. Copied from alert_large_flows.py, where it works only because that file uses logging.getLogger("alert_large_flows") directly. Either drop the flag or apply it with logger.setLevel(...).
Not verified: the Envio endpoint returned HTTP 500 for every query during this review, so the schema field names/types (sender, owner, receiver, and the blockNumber comparison type) could not be checked against the live indexer. Worth a manual smoke run before merge.
Optimizations (nice-to-have)
Steady-state this is ~18 HTTP calls per run, almost always returning zero rows. It can be 2.
Kong: 6 requests → 1, cached (protocols/yearn/kong.py) — monitor_chain calls fetch_kong_parent_vaults(chain) once per chain. chainId is an optional argument on Kong's vaults query and is also selectable as a field, so vaults(v3: true, yearn: true, vaultType: 1) with no chainId returns all 117 parent vaults across every chain in a single request. The list changes on the order of weeks, so it should also go through utils/disk_cache.DiskCache with a multi-hour TTL.
Envio: up to 12 requests → 1 (alert_small_parent_flows.py:298) — one request per (chain, flow_type), each with its own 30s timeout. alert_large_flows.py:309 already has the pattern: deposits:/withdrawals: aliases in one document with chainId: { _in: $chainIds }. Per-chain cursors are still expressible — replace the two-branch _or with one branch pair per chain ({ chainId: {_eq: 1}, blockNumber: {_gt: ...} }, …).
Half the default chain list can never alert (alert_small_parent_flows.py:376) — live Kong data for active (non-retired, non-hidden) vaultType: 1 vaults: Mainnet 15, Katana 6, Base 2, Berachain 2, and zero on Optimism, Polygon and Arbitrum. Those three cost a Kong round-trip plus a logger.warning("No active parent vaults returned for %s") every run. Deriving the chain set from the single all-chain Kong call above drops them and picks up Berachain at the same time.
alert_small_parent_flows.py:310 — one SQLite connect+commit per event.save_cursor → store.state_set → _connect() opens a fresh connection, runs PRAGMA journal_mode=WAL, re-checks schema, writes and commits — per event. A first-run backfill or a busy page does that 1000 times. Batch to once per page, or hold one connection open for the loop. (Tradeoff: a possible duplicate alert if the process dies mid-page.)
On cadence — no change proposed, just noting for later: nothing here needs to be hourly (already-mined historical events, no on-chain reads, persistent cursor), so a longer interval would be safe once the cursor-floor bug above is fixed. As written it wouldn't be, and --lookback-seconds would need to be ≥ 2× any new interval.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.