agents/skills/injectable/l1/cross-environment-semantic-drift/SKILL.md
L1 trigger - audits L1/L2 boundary bugs, precompile context assumptions, integer width mismatches at environment boundaries, and EVM-on-non-EVM drift.
npx skillsauth add plamentsv/plamen cross-environment-semantic-driftInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
L1 trigger:
L1_PATTERN=trueAND (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-externalordepth-state-traceLanguage: Go, Rust, Solidity (for precompiles), C++ Finding prefix:[XE-N]Status: v0.1 draft, Round 4 exemplars pending
Recon detects ONE of the following:
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.
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.
| 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.
For each boundary, apply the four-question checklist:
Each side of the boundary has an invariant. Examples:
sum(balances) == initial_supply - burned + mintedsum(host_balances) == host_total_supplymsg.sender is the callerList both invariants explicitly.
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}]
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}]
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}]
If the boundary crosses Rust/Go ↔ C/C++/CUDA instead of one VM ↔ another, enumerate every integer type at the ABI:
long / unsigned longsize_tCheck 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}]
If the target is a fork of an upstream EVM client (op-geth, op-reth), the highest-leverage analysis is diff-based:
git diff upstream/main...HEAD -- core/vm/Many L2 bugs are "upstream-behavior-X was changed to Y to support L2 feature Z, but the change broke invariant W." This pattern catches the whole class.
Tag: [XE-FORK:{opcode}:{upstream}:{fork}:{invariant-broken}]
Precompiles and cross-environment calls rely on stable encoding:
Tag: [XE-ENCODE:{drift}]
For optimistic or zk rollups:
| State | Test | Expected | Observed | |---|---|---|---| | Max u256 value across u128 boundary | send type(uint256).max | rejected or wrapped deterministically | | | SELFDESTRUCT in cross-env contract | destroy + transfer | consistent on both sides | | | DELEGATECALL to native precompile | call from malicious contract | context preserved or rejected | | | Zero-value cross-boundary call | value = 0 | no state change on either side | | | Deposit replay | same deposit id twice | rejected | | | Withdrawal without state inclusion | withdrawal with invalid proof | rejected | |
[DIFF-PASS] (differential against upstream) > [CONFORMANCE-PASS] > [LSP-TRACE]Optimism SELFDESTRUCT infinite ETH ($2,000,042 bounty, Saurik, February 2022) — OVM 2.0 handling of SELFDESTRUCT with self as beneficiary doubled the balance each call instead of burning it. Caused by skipping the upstream geth branch that handles self-referential destruct. THE canonical cross-env bug. Saurik writeup; Optimism disclosure. Skill catch point: Section 3 (Differential diff pattern) — for every L1 fork, produce a delta-diff against upstream and manually review every opcode-handler change.
Moonbeam delegatecall precompile bypass ($1M + $50k bounty, pwning.eth, May 2022) — custom precompiles (XC-20, staking, democracy) did not distinguish CALL from DELEGATECALL. Malicious contract could DELEGATECALL the precompile, impersonate msg.sender, and access precompile storage of any user. Could drain ~$100M of liquidity. Immunefi bugfix review; Moonbeam patch notice. Skill catch point: Section 2 Q3 (context inheritance) — for every precompile, test whether DELEGATECALL invocation is handled. The direct answer-under-test is context.call_type() != DELEGATECALL at entry.
Polkadot Frontier uint256→u128 truncation ($1M bounty, pwning.eth, June 2022) — Substrate uses u128 balances; Frontier truncated EVM u256 transfer amounts. transferFrom(type(uint128).max + 1) truncated to 0 but bypassed validation. Attacker received huge credit. Affected Moonbeam, Astar, Acala — ~$200M at risk. Immunefi bugfix review; pwning.eth writeup. Skill catch point: Section 2 Q4 (integer width) — validation must run against source value, not truncated value.
Astar Network Frontier integer truncation ($50k bounty, Zellic, November 2023) — same class as #3 but recurred in Astar after the Polkadot Frontier patch. Demonstrates that per-integration checks are not enough — the skill must demand an audit of the Frontier library itself and every upstream consumer. Zellic writeup; Immunefi bugfix review. Skill catch point: dependency-audit-nodeclient — produce reverse-dep graph of shared crypto/VM libraries; any library affecting ≥2 L1/L2 networks is a critical-review target.
Starknet L1-L2 felt252 address mismatch — Ethereum addresses are 160-bit; Starknet addresses are felt252 (2^251 + 17·2^192). L1→L2 messages passing addresses may map valid L1 addresses to null or unexpected L2 addresses due to field truncation. Cairo Security Flaws — oxor.io. Skill catch point: Section 2 Q4 — any implicit type conversion on an address/identifier across environments is a finding.
Insert as new Section 3b: The Moonbeam/Astar/Acala/Polkadot-Frontier chain of four bounties across 18 months shows this class is systemic, not incidental. Per-integration audits missed it. The skill must:
paritytech/frontier for Substrate-EVM chains, custom bridge contracts for L2s)type(uint256).max, type(uint128).max + 1, type(uint64).max + 1 at the boundaryTag: [XE-SHARED-LIB:{library}:{consumers}]
precompile / precompiled_contract implementationsexecution-client-hardening (opcode semantics), light-client-proof-verification (rollup proof verification)depth-external, depth-state-tracedocs/l1-mode/severity-matrix.mddevelopment
Prepare Solidity projects for a security audit — test coverage, test quality, NatSpec docs, code hygiene, dependency health, best-practice enforcement, deployment readiness, and project documentation checks. Generates a scored Audit Readiness Report and optionally runs static analysis. Trigger on: "prepare for audit", "audit readiness", "pre-audit check", "audit prep", "NatSpec check", or any request to review a Solidity codebase before a security review.
development
Launch the Plamen deterministic Web3 security audit pipeline
development
Run the Plamen smart-contract audit wizard in Codex
testing
Launch the Plamen deterministic L1 infrastructure audit pipeline