Skip to content

feat(yvusd): add yvUSD vault monitoring#194

Open
spalen0 wants to merge 6 commits into
mainfrom
yvusd
Open

feat(yvusd): add yvUSD vault monitoring#194
spalen0 wants to merge 6 commits into
mainfrom
yvusd

Conversation

@spalen0
Copy link
Copy Markdown
Collaborator

@spalen0 spalen0 commented Mar 29, 2026

Summary

  • Add monitoring script for yvUSD vault (closes Yvusd monitoring #193)
  • APY anomaly detection: alerts when unlocked APY > locked APY for >6 hours (incentive misalignment), and when any strategy has negative APR
  • CCTP bridging delays: checks cross-chain strategy lastReport timestamps, alerts if stale (>48h)
  • Flashloan liquidity: checks Morpho borrow positions vs available liquidity (Balancer vault + Morpho market) for looper strategy unwinding
  • Large cooldown requests: scans CooldownStarted events on LockedyvUSD, alerts on >$100K requests

Strategy discovery

Looper and cross-chain strategies are discovered dynamically from the yvUSD APR API, so no hardcoded strategy list to maintain.

Contract ABIs

Minimal ABIs for:

  • Yearn V3 Vault (strategies(), totalAssets())
  • Morpho Blue (market(), position())
  • LockedyvUSD (CooldownStarted event, getCooldownStatus(), totalSupply())
  • V3 Tokenized Strategy (lastReport(), totalAssets() — used for remote-side CCTP health checks)

Test plan

  • Verify API fetch works against yvusd-api.yearn.fi/api/aprs
  • Verify on-chain calls succeed with a mainnet RPC
  • Verify remote lastReport() / totalAssets() calls succeed on Arbitrum (0x78b7…, 0xBCf0…)
  • Verify cross-chain looper discovery picks up the live ~$100K Arbitrum syrupUSDC position
  • Confirm get_logs snake-case kwargs work on web3 7.x
  • Confirm CooldownStarted event signature matches LockedyvUSD contract
  • Cache persistence covered by unit test (test_does_not_advance_cache_when_log_fetch_fails)
  • Add to hourly GitHub Actions workflow

🤖 Generated with Claude Code

spalen0 and others added 4 commits March 29, 2026 11:20
Add monitoring for yvUSD vault covering:
- APY inversion detection (unlocked > locked for extended periods)
- Negative strategy APR alerts
- CCTP cross-chain strategy staleness detection
- Flashloan liquidity checks for Morpho looper unwinding
- Large LockedyvUSD cooldown request detection

Closes #193

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move yvusd monitoring into yearn/ to match project conventions:
- yearn/yvusd.py (was yvusd/main.py)
- yearn/abi/ for contract ABIs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@spalen0 spalen0 marked this pull request as ready for review March 29, 2026 20:01
Comment thread yearn/yvusd.py Outdated
loopers = [
s
for s in strategies
if s.get("meta", {}).get("type") in LOOPER_STRATEGY_TYPES
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Current live API data puts the only non-zero looper exposure behind a top-level cross-chain strategy whose remote_vault_type == "morpho-looper" and whose market id lives in remote_meta.market_id. This filter returns zero strategies today, so the monitor logs No active Morpho looper strategies found while the vault still has >$100M of remote looper debt. If the goal is to cover yvUSD unwinding risk end-to-end, we probably need to include the cross-chain case here.

Comment thread yearn/yvusd.py Outdated
logger.info("Scanning blocks %d to %d for cooldown events", from_block, current_block)

try:
events = locked.events.CooldownStarted.get_logs(fromBlock=from_block, toBlock=current_block)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

With web3==7.x this call expects from_block / to_block, not camelCase kwargs. As written it raises TypeError (unexpected keyword argument fromBlock) and returns early, so the cooldown monitor never scans any events in production.

Comment thread yearn/yvusd.py Outdated
remote_counterpart
).call()
except Exception as e:
logger.warning("Could not fetch remote counterpart state for %s: %s", name, e)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I tried this against the current API metadata and both Arbitrum remote vaults (0x78b7… and 0xBCf0…) revert on strategies(remote_counterpart). Because we only log and continue, the CCTP health check quietly disables itself for those strategies. Can we either use the call/ABI that matches these remote vaults or surface an actual alert when the remote lookup fails?

… kwargs

- Remote cross-chain "vaults" (0x78b7…, 0xBCf0…) are V3 tokenized
  strategies and revert on strategies(); switch to direct lastReport() /
  totalAssets() calls and surface a MEDIUM alert on lookup failure instead
  of silently disabling the CCTP health check.
- Flashloan liquidity check now picks up cross-chain wrappers whose
  remote_vault_type is a looper (borrower = remote_vault on the remote
  chain) and runs per-chain via LOOPER_CHAIN_CONFIG (mainnet + Arbitrum).
- events.CooldownStarted.get_logs now uses snake_case from_block /
  to_block for web3 7.x; camelCase raised TypeError and silently returned.
- Add morpho/abi/morpho_blue.json — the previous shared-abi refactor
  pointed ABI_MORPHO at the MetaMorpho vault ABI, which lacks
  market()/position() and would have raised ABIFunctionNotFound.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

Yvusd monitoring

1 participant