Skip to content
Security
Skill

/cross-environment-semantic-drift

L1 trigger - audits L1/L2 boundary bugs, precompile context assumptions, integer width mismatches at environment boundaries, and EVM-on-non-EVM drift.

From plugin
plamen
276160 skills12 agents4 commands
Install
$ npx -y skills add PlamenTSV/plamen --skill cross-environment-semantic-drift --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-environment-semantic-drift

Context preview

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

L1 trigger - audits L1/L2 boundary bugs, precompile context assumptions, integer width mismatches at environment boundaries, and EVM-on-non-EVM drift.

SKILL.md

cross-environment-semantic-drift.SKILL.md
name: "cross-environment-semantic-drift"
description: "L1 trigger - audits L1/L2 boundary bugs, precompile context assumptions, integer width mismatches at environment boundaries, and EVM-on-non-EVM drift."

Injectable Skill: Cross-Environment Semantic Drift

> **L1 trigger**: `L1_PATTERN=true` AND (fork of an EVM execution client OR L2-rollup detected OR EVM-on-non-EVM runtime OR precompile implementation in a non-EVM host detected) > **Inject Into**: `depth-external` or `depth-state-trace` > **Language**: Go, Rust, Solidity (for precompiles), C++ > **Finding prefix**: `[XE-N]` > **Status**: v0.1 draft, Round 4 exemplars pending

Orchestrator Decomposition Guide

  • Sections 1, 2: depth-external (boundary enumeration)
  • Section 3: depth-state-trace (semantic diff tracing)
  • Section 4: depth-edge-case (integer width / encoding boundaries)

When This Skill Activates

Recon detects ONE of the following:

1. Target is a **fork of an EVM execution client** (op-geth, op-reth, arbitrum-nitro, base-node) 2. Target is an **L2 rollup** that re-implements EVM semantics (Optimism OVM, Arbitrum AVM, early zkEVMs) 3. Target runs **EVM on a non-EVM host** (Moonbeam on Polkadot-SDK, Frontier on Substrate, Neon on Solana) 4. Target implements **precompiles** that wrap native-host functionality 5. Target has **integer-width boundaries** between environments (128-bit Substrate balance vs 256-bit EVM value)

This is the most consequential "new class" skill: the most famous L1 bounties (Saurik's Optimism, pwning.eth's Moonbeam and Polkadot Frontier) are all in this category.

1. Boundary Enumeration

Map every semantic boundary in the target. A boundary is any place where code written against one execution model calls into code written against another.

Boundary types

| Type | Example | Risk | |---|---|---| | **EVM ↔ host chain balance** | Optimism OVM_ETH wraps native ETH | Double-counting, wrong-account credit | | **EVM ↔ precompile** | Moonbeam ERC-20 precompile wrapping GLMR | Call context confusion, allowance abuse | | **EVM-256 ↔ host-N** | 256-bit value to 128-bit host balance | Truncation, wraparound | | **Rollup sequencer ↔ L1 inbox** | Optimism deposit tx | Replay, double-credit | | **Bridge contract ↔ bridge relay** | Any canonical bridge | Message forging, replay | | **L2 state root ↔ L1 dispute game** | Arbitrum, Optimism | Invalid state root acceptance |

Write the boundary map to `scratchpad/xenv_boundaries.md` before proceeding.

2. Per-Boundary Semantic Check

For each boundary, apply the four-question checklist:

Q1: What invariant does each side maintain?

Each side of the boundary has an invariant. Examples:

  • EVM: `sum(balances) == initial_supply - burned + minted`
  • Host: `sum(host_balances) == host_total_supply`
  • Precompile: `msg.sender is the caller`

List both invariants explicitly.

Q2: Does crossing the boundary preserve both invariants?

Trace a value / state update from one side to the other. Does the accounting on both sides end consistent?

**Known exemplar (Optimism SELFDESTRUCT)**: when a contract SELFDESTRUCTed, Optimism zeroed the contract's internal balance but forgot to remove the balance from the OVM_ETH ERC-20 total → the value was duplicated.

**Check pattern**: for every cross-boundary operation, write out pre-state and post-state on BOTH sides and verify conservation.

Tag: `[XE-CONS:{boundary}:{invariant}:{break-path}]`

Q3: Does the boundary inherit the caller's context correctly?

Precompiles, system contracts, and host-function wrappers all face the question: "what does `msg.sender` / `caller` mean when I'm called via DELEGATECALL?"

**Known exemplar (Moonbeam delegatecall-to-precompile)**: pwning.eth showed that a malicious contract could DELEGATECALL into Moonbeam's native-token precompile, which then used the caller's identity without realizing the call-context had been rewritten. Attacker impersonated liquidity pools and drained them.

**Check pattern**: for every precompile / system contract, list what it does with `msg.sender`. Then ask: does it treat DELEGATECALL correctly? If the answer is "it doesn't know or doesn't check," that's a finding.

Tag: `[XE-CONTEXT:{precompile}:{delegatecall-handling}]`

Q4: Do integer widths match across the boundary?

Cross-environment value passing often crosses integer-width boundaries. EVM uses uint256; Substrate uses u128; Solana lamports are u64.

**Known exemplar (Polkadot Frontier 128-bit truncation)**: msg.value is uint256 in Solidity; Substrate balance is u128. Frontier truncated the top bits when passing value into the host call. An attacker passed `2^128 + X`, the host saw `X`, but the contract saw `2^128 + X` — the contract credited itself with a massive amount while only `X` actually moved.

**Check pattern**: for every value passed across the boundary, list the source width and destination width. If source > destination, check for an explicit range check that rejects values above the destination's max. **Silent truncation is always a bug.**

Tag: `[XE-WIDTH:{src-width}→{dst-width}:{check-status}]`

Q4a: FFI / ABI integer width

If the boundary crosses Rust/Go ↔ C/C++/CUDA instead of one VM ↔ another, enumerate every integer type at the ABI:

  • `long` / `unsigned long`
  • `size_t`
  • pointer-sized integers

Check whether the code assumes LP64 semantics while the deployment target may be LLP64 (for example Windows). Any 64-bit semantic value passed through `long`/`unsigned long` without an explicit width check is a truncation finding.

Tag: `[XE-FFI-WIDTH:{type}]`

3. Differential Diff Pattern (for forks)

If the target is a fork of an upstream EVM client (op-geth, op-reth), the highest-leverage analysis is diff-based:

1. `git diff upstream/main...HEAD -- core/vm/` 2. For every modified opcode or precompile, trace the behavior difference 3. Ask: does the modification preserve the upstream's invariants on both sides?

Many L2 bugs are "upstream-behavior-X was changed

Read more
Ships withplamen

Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.

Get the whole plugin