Skip to content
Security
Skill

/cross-contract-state

Detect cross-contract state inconsistency — two or more contracts sharing a token, oracle, or price feed where one mutates and another reads stale, cached state that drifts from source of truth, non-atomic multi-contract updates, accounting that assumes synchronized state, and

From plugin
rugproof
952 skills23 agents45 commands4 hooks
Install
$ npx -y skills add omermaksutii/RugProof --skill cross-contract-state --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/cross-contract-state

Context preview

The summary Claude sees to decide when to auto-load this skill.

Detect cross-contract state inconsistency — two or more contracts sharing a token, oracle, or price feed where one mutates and another reads stale, cached state that drifts from source of truth, non-atomic multi-contract updates, accounting that assumes synchronized state, and

SKILL.md

cross-contract-state.SKILL.md
name: cross-contract-state
description: Detect cross-contract state inconsistency — two or more contracts sharing a token, oracle, or price feed where one mutates and another reads stale, cached state that drifts from source of truth, non-atomic multi-contract updates, accounting that assumes synchronized state, and reads during callbacks. Activate whenever a system spans multiple contracts that must agree on a value but update at different times.

Cross-contract state consistency detection

When this applies

Trigger on any of:

  • A value (price, total supply, share rate, debt) lives in contract A but is read by B
  • Cached/mirrored state (`cachedRate`, `lastTotalAssets`) that must be refreshed
  • Multi-contract operations that mutate several contracts non-atomically
  • Modules/plugins/hooks that read core state mid-update (read-during-callback)
  • Proxy + implementation, or vault + strategy, or controller + market splits
  • Accounting assuming "A and B are always in sync"

Detection patterns

Stale read of another contract's mid-update state (HIGH)

// Vault.deposit() transfers to Strategy, then:
uint256 tvl = strategy.totalAssets();   // ← strategy hasn't accounted the deposit yet
shares = amount * totalSupply / tvl;    // wrong denominator

**Signal:** B reads A while A's update is incomplete (or vice versa). Share/price math uses a denominator that's about to change, letting an attacker mint mispriced shares. This is the read-only-reentrancy family generalized to ordinary call ordering.

Cached value drift (HIGH)

uint256 public cachedPrice;            // updated by poke()
function valueOf(uint256 amt) external view returns (uint256) {
    return amt * cachedPrice / 1e18;   // ← may be hours stale
}

**Signal:** a mirrored value with no freshness guarantee or no atomic refresh-before-use. Trades/loans price off a cache that diverged from the source contract.

Non-atomic multi-contract update (HIGH)

registry.setActive(id, true);
// external call here can observe registry=active but vault=uninitialized
vault.initialize(id);

**Signal:** state spread across contracts is updated in sequence; a reentrant or interleaved call observes a half-applied transaction (one contract updated, the other not). Invariants that span both contracts are temporarily violated.

Shared dependency divergence (MEDIUM / HIGH)

Two contracts both read the same oracle/token but at different blocks or via different cached copies, then reconcile assuming equality. **Signal:** `assert(A.totalSupply() == B.mirroredSupply())`-style assumptions with independent update paths.

Read-during-callback (HIGH)

A hook/plugin invoked mid-operation calls back and reads the core contract's not-yet-finalized accounting — the Curve/Balancer read-only reentrancy shape, but also plain composability (e.g. an ERC-4626 vault read by a money-market during the vault's own deposit).

Severity rubric

| Pattern | Severity | Notes | |---|---|---| | Stale cross-contract read in share/price math | **High** | Mispriced mint/redeem | | Read-during-callback of unfinalized state | **High** | Read-only reentrancy class | | Non-atomic multi-contract invariant break | **High** | Half-applied state observable | | Cached value drift used for valuation | **High** | Stale pricing | | Synchronized-state assumption across modules | **Medium** | Reconciliation error |

Remediation patterns

1. **Single source of truth** — derive values from one authoritative contract at read time; avoid mirrored copies, or make the mirror push-updated atomically in the same tx. 2. **Update-then-read ordering** — ensure A finalizes accounting before B reads; apply CEI across the call boundary, not just within a function. 3. **Atomic multi-contract updates** — batch via a single entrypoint / multicall that completes all writes before any external observation; guard with a system-wide reentrancy lock. 4. **Freshness gates on caches** — `require(block.timestamp - lastUpdate <= maxAge)` or refresh-on-read. 5. **Reentrancy-guard the read path too** (Balancer `ensureNotInVaultContext` / Curve lock) when callbacks can observe state.

False-positive notes

  • A mirror that is push-updated atomically in the same transaction as its source is consistent — don't flag.
  • Independent contracts that never reconcile or compare state aren't subject to this; the risk needs a shared invariant.
  • Eventually-consistent designs that explicitly tolerate drift (and bound it) are by-design — note the assumption.

Related

  • [[reentrancy]] — read-only reentrancy is the callback variant of this
  • [[oracle-redundancy]] — shared feed staleness/divergence
  • [[storage-layout]] — proxy/implementation storage must also stay consistent
Read more
Ships withrugproof

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

Get the whole plugin

Other skills on rugproof.