example-fork-detection
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect oracle fallback and redundancy failure modes (distinct from price manipulation) — single point of failure, missing staleness/heartbeat/deviation checks, an "all oracles down" path that reverts or silently returns stale/zero, missing L2 sequencer-uptime feed, and absent
$ npx -y skills add omermaksutii/RugProof --skill oracle-redundancy --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/oracle-redundancyContext preview
The summary Claude sees to decide when to auto-load this skill.
Detect oracle fallback and redundancy failure modes (distinct from price manipulation) — single point of failure, missing staleness/heartbeat/deviation checks, an "all oracles down" path that reverts or silently returns stale/zero, missing L2 sequencer-uptime feed, and absent
name: oracle-redundancy description: Detect oracle fallback and redundancy failure modes (distinct from price manipulation) — single point of failure, missing staleness/heartbeat/deviation checks, an "all oracles down" path that reverts or silently returns stale/zero, missing L2 sequencer-uptime feed, and absent circuit breakers. Activate whenever code reads a price/rate feed, especially Chainlink latestRoundData, with fallbacks or on an L2.
Trigger on any of:
(, int256 price,,,) = feed.latestRoundData(); // ignores updatedAt & answeredInRound require(price > 0); return uint256(price);
**Signal:** no `updatedAt` freshness gate. A frozen feed (feed deprecated, node outage, or a market-wide halt) returns the last value forever; protocol prices off stale data. Require `block.timestamp - updatedAt <= heartbeat` and `answeredInRound >= roundId`.
One feed, no fallback, no degradation plan. If that feed is deprecated or returns garbage, the protocol is bricked or mispriced. **Signal:** a hard dependency on exactly one external address with no alternative path.
try feed.latestRoundData() returns (...) { ... }
catch { return lastGoodPrice; } // ← stale, or worse:
catch { return 0; } // ← 0 collateral value = mass liquidation, or free mint**Signal:** the catch path masks failure. Returning `0` can make collateral worthless (mass liquidation) or debt free; returning a stale cached price keeps trading on bad data. Failures must surface, not be swallowed.
// Arbitrum/Optimism: no check of the sequencer uptime feed (, int256 answer, uint256 startedAt,,) = sequencerUptime.latestRoundData(); require(answer == 0 && block.timestamp - startedAt > GRACE_PERIOD);
**Signal:** on an L2, if the sequencer was down, Chainlink prices are stale on resume. Without the uptime feed + grace period, the first post-downtime block liquidates everyone at stale prices. Chainlink documents this exact guard.
The aggregator handles primary-down but has no defined behavior when *every* source is stale/reverting — it either reverts (DoS) or falls through to an uninitialized default. Define an explicit safe-halt (pause) rather than trade on uncertainty.
Accepting any reported price without comparing primary vs secondary deviation, so a single compromised/erroneous feed is trusted outright.
| Pattern | Severity | Notes | |---|---|---| | Fallback returns 0 on failure | **High** | Mass liquidation / free mint | | Missing staleness/heartbeat gate | **High** | Trades on frozen price | | Single feed, no fallback | **High** | Brick / misprice on outage | | Missing L2 sequencer-uptime check | **High** | Stale-price liquidation on resume | | "All down" path reverts (DoS) vs safe-halt | **Medium** | Availability vs safety tradeoff | | No primary/secondary deviation check | **Medium** | Single bad feed trusted |
1. **Validate every read:** `updatedAt` freshness vs a per-feed heartbeat, `answeredInRound >= roundId`, `price > 0`, and a sane min/max bound. 2. **L2 sequencer-uptime feed** + `GRACE_PERIOD` before trusting prices post-downtime (Chainlink reference pattern). 3. **Real redundancy:** primary + independent secondary (e.g. Chainlink + Uniswap V3 TWAP / Pyth) with a deviation threshold; disagreement → pause, not pick-one. 4. **Fail closed:** when all sources are stale/invalid, **pause** the affected operations rather than returning 0, last price, or reverting unboundedly. 5. **Circuit breaker** on max per-block deviation to absorb wicks.
Rugproof your code before someone else does. 🌐 Live site: omermaksutii.github.io/RugProof 📦 Latest: v1.0.0 — 45 commands · 23 agents · 45 skills · 13 MCP servers · tested, offline-first, with rule packs, a benchmark, non-EVM coverage, and post-deploy
Repo: omermaksutii/RugProof
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom storage slots, returns bools via…
Detect front-runnable ownership initialization in Solady Ownable / OwnableRoles. Solady's `_initializeOwner` is a guarded one-time setter (it reverts with…
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove deliberately skip the EXTCODESIZE…
Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates (BeforeSwapDelta, afterSwap hookDelta,…
Detect Uniswap V4 hooks whose address-encoded permission flags don't match the callbacks the hook actually implements. In V4 the hook's permissions live in the…