/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.
$ npx -y skills add PlamenTSV/plamen --skill cross-environment-semantic-drift --agent claude-codeHow 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.mdname: "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
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
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.
Repo: PlamenTSV/plamen
Other skills on plamen.
- /ability-analysis
Trigger Pattern Always (Aptos Move) - foundational security check - Inject Into Breadth agents, depth agents
Open skill - /bit-shift-safety
Trigger Pattern Always (Aptos Move) - Move VM aborts on shift = bit width - Inject Into Breadth agents, depth-edge-case
Open skill - /centralization-risk
Trigger Protocol has privileged roles (admin, operator, governance, resource account owner) - Covers Single points of failure, privilege escalation, external governance dependen...
Open skill - /cross-chain-timing
Trigger Pattern wormhole|layerzero|ccip|bridge|cross_chain|vaa|guardian|emitter|relay|remote_chain|payload|nonce.sequence - Inject Into Breadth agents, depth-external
Open skill - /dependency-audit
Trigger EXTERNAL_LIB flag detected (protocol uses third-party Move dependencies) - Used by Breadth agents, depth-external
Open skill - /economic-design-audit
Trigger Pattern MONETARY_PARAMETER flag (required) - Inject Into Breadth agents (merged via M4 hierarchy)
Open skill

