Root Cause
Modern EVM wallets (MetaMask, Rabby, etc.) simulate transactions before submission to show the user an expected outcome. Simulation-phishing contracts exploit a fundamental asymmetry: the code path executed during simulation differs from the code path executed during on-chain execution, because the two environments have different values for state variables, block parameters, or gas conditions.
A phishing contract might return funds to the caller in the simulation branch, but reroute them to an attacker-controlled address when the real transaction executes.
// Simplified storage-control phishing pattern
contract SimPhish {
address owner = 0xAttacker;
bool simulated = true; // set to false after first real tx
function transfer(address to, uint256 amt) external {
if (simulated) {
// Wallet simulator sees this branch: legitimate transfer
_transfer(to, amt);
} else {
// Real execution: steal funds
_transfer(owner, amt);
}
}
}
The SimGuard paper identifies six control classes based on which blockchain variable differentiates simulation from execution:
| Class | Discriminating Variable | Example Trigger |
|---|---|---|
storage-control | A storage slot the attacker pre-sets | sstore(0, 0) in constructor |
external-control | Call to an oracle or external contract | Different return between sim and exec |
Gas-control | gasleft() value | Wallets simulate with full gas; real txs may differ |
Gasprice-control | tx.gasprice | Wallets often simulate at gasprice=0 |
blocknumber-control | block.number | Simulation uses pending block; real tx uses mined block |
timestamp-control | block.timestamp | Simulation timestamp ≠ execution timestamp |
Attack Steps
| Step | Detail |
|---|---|
| 1 | Attacker deploys phishing contract (storage or block-parameter initialized to "simulation-mode") |
| 2 | Victim is lured to interact (token approval, transfer, NFT mint) |
| 3 | Wallet simulator shows benign preview (funds arrive, nothing drained) |
| 4 | Victim confirms; real on-chain tx takes the drain branch |
| 5 | Victim's tokens or ETH are routed to attacker's EOA |
Impact
- Chains affected: Ethereum (480 contracts), Avalanche (3,316), Polygon (315), BSC (293)
- Total phishing contracts: 4,224 (identified by SimGuard)
- Victims: 5,742 identified
- Estimated total losses: ~$3,480,000 USD
- Loss distribution: 91.5% on Ethereum despite Ethereum having fewer contracts — Ethereum contracts hold more value per interaction
- Timeline: First appeared August 2024; peak activity March–May 2025 (100+ contracts/day across chains); still active as of paper publication (July 30, 2026)
Lessons for Auditors
- Audit for simulation–execution divergence. Any contract that checks
block.number,block.timestamp,tx.gasprice,gasleft(), or an external oracle as a branch condition may behave differently under simulation vs. real execution. - Storage-initialization tricks. A constructor that sets a "seen" flag in storage, then flips it in the first real call, is a red flag. Auditors should trace whether
SLOAD/SSTOREcould differentiate simulated vs. live execution. - Wallet providers must harden simulators. Wallets should run simulations at realistic gas prices, randomize
block.timestampwithin expected ranges, and detect bifurcated fund-flow paths (sim returns X; real code path returns nothing to caller). - Bytecode-level detection is viable. SimGuard's approach (identifying
JUMPIbranches where one path sends funds to caller, the other to an external address) can be integrated into deployment-time screening by block explorers or wallet extensions. - ERC-20
approveflows are highest risk. Approvals grant long-lived access; simulation phishing targetingapprovecalls can drain victims long after the initial interaction.