Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Implement a Transaction Simulation Environment for Attack Testing

A technical guide to creating a forked mainnet environment for simulating multi-step DeFi exploits and testing security mitigations using Foundry and Tenderly.
Chainscore © 2026
introduction
GUIDE

How to Implement a Transaction Simulation Environment for Attack Testing

A transaction simulation environment is a sandboxed system that executes blockchain transactions in a controlled, risk-free setting to analyze outcomes and identify vulnerabilities before they reach mainnet.

A transaction simulation environment is a foundational tool for security research and smart contract development. It allows you to execute a transaction against a local fork of a blockchain state, such as Ethereum or a Layer 2, without spending real gas or risking funds. This is critical for testing attack vectors like front-running, sandwich attacks, or reentrancy in a safe, reproducible manner. Tools like Foundry's forge and Hardhat Network provide the core infrastructure for this, enabling you to fork mainnet at a specific block and interact with live contracts locally. The primary output is a detailed trace of the transaction's execution path, state changes, and gas consumption.

Setting up a basic simulation involves forking a live network and crafting a malicious transaction. Using Foundry as an example, you start by forking Ethereum mainnet. The cast command can then be used to simulate a specific transaction by its hash, or you can write a Solidity test to programmatically send a custom payload. The environment replicates all state, including token balances and contract storage, allowing you to test interactions with protocols like Uniswap or Aave as they exist on-chain. This method is indispensable for analyzing MEV bot strategies or verifying the safety of a new DeFi interaction before signing it with your wallet.

For advanced attack simulation, you need to move beyond single transactions and model multi-block, multi-transaction scenarios. This involves scripting a sequence of actions where an attacker's transaction is positioned relative to victim transactions. Libraries like eth-brownie or custom scripts using Ethers.js on a forked Hardhat node allow this. A key technique is impersonation, where you grant your local test account control over any on-chain address, enabling you to simulate transactions from whales or specific contracts. This is how you test flash loan attacks that require manipulating prices across several protocol calls within a single block.

Analyzing simulation results requires examining more than just success or failure. You must inspect the transaction trace for unexpected state changes, pinpoint gas-guzzling operations, and identify reverted inner calls. Foundry's debug traces and Hardhat's console.log are essential here. The goal is to quantify the attack's impact: calculate the profit in USD, determine the minimum required capital (e.g., flash loan size), and note any side-effects on other users. This analysis transforms a theoretical exploit into a validated, measurable finding, which is the basis for a proof-of-concept report or mitigation strategy.

Integrating simulation into a continuous security workflow automates vulnerability detection. You can create a test suite that regularly forks the latest block and runs simulations against known attack patterns on your protocol's contracts. This regression testing can catch new vulnerabilities introduced by updates or shifts in the external DeFi ecosystem. Furthermore, by using CI/CD pipelines with tools like GitHub Actions, these security checks run automatically on every pull request. This proactive approach, championed by teams at OpenZeppelin and Trail of Bits, significantly reduces the risk of deploying exploitable code.

prerequisites
PREREQUISITES AND SETUP

How to Implement a Transaction Simulation Environment for Attack Testing

This guide details the essential tools and initial configuration required to build a local, isolated environment for simulating and analyzing malicious blockchain transactions.

Before simulating attacks, you must establish a controlled testing environment. The core component is a local blockchain node. Foundry's Anvil is the industry standard for Ethereum Virtual Machine (EVM) chains, as it provides a deterministic, forkable local network. Alternatively, Hardhat Network offers similar functionality with deep integration for Solidity development. You will also need a command-line tool for crafting and sending transactions; cast (part of Foundry) is powerful for this, but you can use ethers.js or web3.py scripts. Ensure you have Node.js (v18+) or Rust installed, depending on your toolchain choice.

The next step is to fork a live network state into your local environment. This creates a precise replica of mainnet or a testnet at a specific block, allowing you to test against real contract deployments and token balances. With Anvil, you run anvil --fork-url <RPC_ENDPOINT>. A reliable RPC provider like Alchemy, Infura, or a public service is required. Forking is critical because it lets you simulate attacks on actual, deployed smart contracts—such as a major DEX or lending protocol—using real-world state data, making your tests far more relevant.

You will need test accounts with funded wallets. Upon startup, your local node (e.g., Anvil) provides a set of pre-funded private keys. Securely export these keys or their mnemonic for use in your simulation scripts. For more complex scenarios, use cast wallet import or the ethers.Wallet class to create and fund additional accounts. Remember, the goal is to simulate an attacker's actions, so you must be able to sign transactions from any address involved in your test case, including victim and attacker wallets.

Structuring your project is key for reproducibility. Create a dedicated directory with subfolders for scripts (/scripts), attack payloads (/payloads), and configuration. Use a .env file to manage sensitive variables like RPC URLs and private keys (never commit this). For Foundry, a foundry.toml config file sets the forked network. For Hardhat, configure hardhat.config.js. This setup allows you to quickly spin up an identical environment, run a simulation, tear it down, and analyze the results without affecting any live network.

key-concepts
BUILDING A TEST ENVIRONMENT

Core Concepts for Simulation

To effectively test for attacks, you need a realistic simulation environment. These core concepts and tools form the foundation for building one.

forking-mainnet-with-foundry
SETUP

Step 1: Forking Mainnet with Foundry Anvil

Learn how to create a local, fork-based test environment to simulate real-world blockchain states for security testing and development.

A mainnet fork is a local copy of the Ethereum mainnet's state at a specific block. Tools like Foundry's anvil command let you spin up a local node that mirrors the live network, including all deployed contracts, token balances, and storage data. This is the foundation for transaction simulation, allowing you to test interactions with protocols like Uniswap V3 or Aave without spending real ETH or risking funds on-chain. You can think of it as a sandboxed, interactive snapshot of the real world.

To start, ensure you have Foundry installed. The core command is anvil --fork-url <RPC_URL>. You must provide a reliable RPC endpoint from a service like Alchemy, Infura, or a public provider. For reproducible tests, pin the fork to a specific block using --fork-block-number. For example, anvil --fork-url https://eth-mainnet.g.alchemy.com/v2/your-key --fork-block-number 19238201 creates a deterministic environment based on that exact historical state. This is crucial for debugging and ensuring test consistency.

Once running, Anvil provides a local RPC endpoint (typically http://127.0.0.1:8545) and ten pre-funded accounts with 10,000 test ETH each. You can now use cast or forge commands, or connect a script or UI like MetaMask (pointed to Localhost 8545), to interact with the forked chain. All read calls will reflect the forked state, and write calls (transactions) will be executed locally, leaving the real mainnet completely untouched. This enables safe attack simulation against live contract logic.

For advanced testing, you can manipulate the forked state. Use anvil_setBalance to modify an account's ETH or anvil_setCode to deploy new contract bytecode at an existing address. This is essential for simulating post-exploit conditions or testing how a protocol behaves if a critical contract is upgraded maliciously. You can also impersonate any account (anvil_impersonateAccount) to send transactions from it, bypassing private key requirements, which is invaluable for testing access control and privilege escalation scenarios.

Integrate this forked environment into your Foundry test suite by setting the fork_url and fork_block_number in your foundry.toml file. This allows forge test to run your Solidity tests against the live forked state. You can write tests that interact with real Compound pools, check oracle prices from Chainlink, or validate slippage calculations on a forked Curve pool. This bridges the gap between unit tests and live on-chain validation, providing high-fidelity security analysis.

replaying-historical-transactions
SIMULATION ENVIRONMENT

Step 2: Replaying and Inspecting Historical Transactions

Learn how to build a local fork of a blockchain to replay and analyze real-world transactions, a foundational technique for security research and protocol testing.

A transaction simulation environment is a local, isolated fork of a live blockchain state. Tools like Foundry's anvil or Hardhat Network allow you to fork mainnet or testnet at a specific block number. This creates a sandboxed replica where you can execute transactions without spending gas or affecting the live network. The command anvil --fork-url $RPC_URL --fork-block-number 18541666 creates a local node with the exact state of Ethereum mainnet at block 18,541,666, which is essential for deterministic replay.

To replay a transaction, you need its raw data: the transaction hash. Using the forked node's RPC endpoint (typically http://localhost:8545), you can call eth_getTransactionByHash to retrieve all details, including input data (the calldata) and from/to addresses. You then use eth_call to simulate the transaction execution. This dry-run returns the result—state changes, emitted events, and potential errors—without broadcasting it, allowing you to inspect the precise on-chain logic that was executed.

For deeper inspection, you must interact with the contracts involved. Use cast (from Foundry) or ethers to decode the transaction's input calldata against the contract's ABI: cast 4byte-decode <input_data>. This reveals the function called and its arguments. To trace internal calls and gas usage, enable verbose tracing with anvil --steps-tracing or use debug_traceTransaction on the forked node. This trace shows every opcode, storage slot change, and internal call, which is critical for understanding complex DeFi interactions or pinpointing exploit steps.

This environment is not just for replay. It's the basis for active testing. You can modify parameters—like user balances, oracle prices, or block timestamps—before replaying a transaction to test "what-if" scenarios. For example, you could increase a victim's token allowance before replaying an approval exploit to see its maximum impact. This method is used by security researchers to validate bug reports, by developers to test upgrade scenarios, and by auditors to verify the root cause of historical hacks in a controlled setting.

scripting-multi-step-attacks
ADVANCED TESTING

Step 3: Scripting Multi-Step Attack Scenarios with Forge

Learn how to use Foundry's Forge to script and execute complex, multi-transaction attack simulations against your smart contracts.

Multi-step attack scenarios are critical for security testing because real-world exploits often involve a sequence of carefully crafted transactions. A simple flash loan attack, for instance, requires at least three steps: borrowing funds, manipulating a price oracle, and profiting from an arbitrage or liquidation. Forge's scripting environment, powered by the forge script command, allows you to write these scenarios in Solidity, giving you full control over the EVM state across multiple blocks. This is superior to single-function unit tests, as it models the temporal and state-dependent nature of on-chain attacks.

To begin, create a new script file (e.g., AttackSimulation.s.sol) in your project's script/ directory. The script is a contract that inherits from Script. Your attack logic resides in the run() function. Use the vm.startBroadcast() and vm.stopBroadcast() cheatcodes to wrap the transactions you want to broadcast to the simulated network. Crucially, you can interact with both your local contract deployments and any mainnet-forked contracts, calling them in any order. For example, you can script an attack that first deposits collateral into a lending protocol, then triggers a price drop via a manipulated oracle, and finally calls the liquidation function.

Forge provides essential cheatcodes for realistic simulations. Use vm.roll(blockNumber) to simulate the passage of time or specific block conditions. The vm.deal(address, amount) cheatcode funds attacker addresses. To simulate a specific actor, use vm.startPrank(attackerAddress). A key feature for multi-step tests is vm.expectRevert(), which allows you to assert that a later step in your attack fails if a prior vulnerability has been patched. This creates a powerful regression test suite. You can find the full cheatcode reference in the Foundry Book.

Here is a simplified code skeleton for a multi-step simulation:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Script, console} from "forge-std/Script.sol";
contract AttackSim is Script {
    function run() external {
        uint256 attackerKey = vm.envUint("PRIVATE_KEY");
        address attacker = vm.addr(attackerKey);
        vm.startBroadcast(attackerKey);
        // Step 1: Execute flash loan from forked Aave
        IAaveLendingPool(aavePool).flashLoan(...);
        // Step 2: Manipulate Uniswap pool via a large swap
        IUniswapV2Pair(pair).swap(...);
        // Step 3: Liquidate undercollateralized position
        ICompoundV2(comptroller).liquidateBorrow(...);
        vm.stopBroadcast();
    }
}

To execute the script, run forge script script/AttackSimulation.s.sol:AttackSim --fork-url $RPC_URL. Use the --broadcast flag if you want to broadcast the transactions to a live network (e.g., for a testnet dry-run), though simulation is the default. Analyze the transaction traces and gas reports printed to the console. For complex logic, consider breaking your attack into modular helper functions within the script contract to improve readability and reusability. This approach transforms security testing from checking single invariants to validating the entire attack surface of your protocol under adversarial conditions.

simulating-with-tenderly
ATTACK TESTING

Step 4: Advanced Simulation with Tenderly

Learn how to build a controlled environment for simulating and analyzing malicious transactions using Tenderly's sandboxed fork.

Transaction simulation is a critical tool for security researchers and developers to test attack vectors in a risk-free environment. Tenderly provides a sandboxed fork feature that creates an isolated, stateful copy of a blockchain at a specific block. This allows you to execute transactions that modify the fork's state without affecting the mainnet or requiring real ETH for gas. You can simulate complex multi-step attacks, test smart contract interactions, and observe the precise state changes and internal calls that occur.

To begin, you'll need a Tenderly account and an API key from your Dashboard. The core of the simulation environment is the TenderlyFork object. Using the Tenderly API or SDK, you can programmatically create a fork, fund simulated accounts with test ETH, and execute transactions. For example, to create a fork of Ethereum mainnet at block 20,000,000, you would call the API endpoint https://api.tenderly.co/api/v1/account/{account}/project/{project}/fork with the appropriate JSON payload specifying the network and block number.

Once your fork is active, you can use its unique RPC URL to connect tools like Hardhat, Foundry, or a custom script. This enables you to deploy contracts, impersonate any account (a powerful feature for testing access control), and send transactions as if you were on a live network. A typical attack simulation workflow involves: 1) creating the fork, 2) impersonating the attacker and victim addresses, 3) funding them with simulated ETH, 4) executing the malicious transaction sequence, and 5) analyzing the transaction trace and final state diff to understand the exploit's mechanics and financial impact.

Tenderly's debugger and transaction tracing are indispensable for post-simulation analysis. The debugger provides a step-by-step view of EVM execution, allowing you to inspect variables and storage at each opcode. The trace shows all internal calls, log emissions, and state changes in a tree structure. This granular visibility helps you pinpoint exactly where a contract's logic fails—whether it's an arithmetic overflow, a reentrancy entry point, or an incorrectly validated caller. You can share these simulation links with your team for collaborative review.

For systematic testing, integrate Tenderly simulations into your CI/CD pipeline or automated testing suite. Using the Tenderly Hardhat plugin, you can write Mocha/Chai tests that run against a forked network. This allows you to assert that certain state changes occur (or that transactions revert) when specific conditions are met. By automating the simulation of known attack patterns against your contracts after each commit, you build a robust defense-in-depth strategy that catches vulnerabilities before deployment.

LOCAL VS. CLOUD

Simulation Tool Comparison: Foundry vs Tenderly

Key differences between a local development framework and a cloud-based platform for transaction simulation.

FeatureFoundry (Local)Tenderly (Cloud)

Execution Environment

Local EVM (Forge)

Cloud-based Forked Network

State Forking

Via RPC URL (e.g., --fork-url)

Managed, persistent forks with UI

Gas & Fee Simulation

Full control via eth_call

Detailed breakdown with visualization

Transaction Tracing

Built-in (--trace) and forge commands

Advanced debugger with step-through UI

Speed for Iteration

< 1 sec (no network latency)

1-3 sec (API call latency)

Cost

Free (compute resources)

Free tier + paid plans for high usage

Integration

CLI, scripts, CI/CD pipelines

Dashboard, API, Webhooks, Alerting

State Persistence

Ephemeral (resets per run)

Fork state saved for days/weeks

testing-security-fixes
SECURITY TESTING

Step 5: Testing Security Measures and Parameter Changes

This guide explains how to build a transaction simulation environment to rigorously test security measures, economic parameters, and potential attack vectors before deploying to mainnet.

A transaction simulation environment is a sandboxed, forked version of a blockchain network where you can execute and analyze transactions without financial risk. The core tool for this is Tenderly, which provides a Web3 Gateway for forking networks like Ethereum Mainnet, Arbitrum, or Polygon. Using a fork allows you to interact with real, deployed smart contracts and simulate transactions with custom parameters, such as altering an account's balance or manipulating the block timestamp. This is essential for testing how protocol changes behave under edge-case conditions that are difficult to replicate on a testnet.

To set up a basic simulation environment, you'll need a development framework like Foundry or Hardhat. After forking a network via Tenderly's RPC URL, you can write test scripts that impersonate any address using vm.prank() in Foundry or hardhat_impersonateAccount in Hardhat. This lets you simulate actions from privileged accounts (like a protocol's governance multisig) or malicious actors. A critical first test is verifying that all security measures—such as pause functions, withdrawal limits, and access controls—work as intended when triggered by an authorized entity.

The real power of simulation lies in parameter stress testing. For a lending protocol, you could simulate a scenario where the price of a collateral asset plummets by 80% in a single block. Using a price oracle mock or manipulating an oracle's reported price directly on the fork, you can test if liquidations trigger correctly and whether the protocol's solvency is maintained. For a DEX, you could test the impact of dramatically increasing swap fee parameters or changing the weights in a Balancer-style pool to ensure the system remains stable and arbitrage-resistant.

Attack vector simulation is the next step. Common tests include: reentrancy attacks on newly added functions, flash loan exploitation to manipulate governance votes or oracle prices, and economic attacks like donation attacks on rebasing tokens or manipulating TWAP oracles. By combining impersonation, state manipulation, and custom transaction ordering, you can construct multi-step attack transactions. Tools like Foundry's forge test with the --fork-url flag allow you to run these simulations and see precise gas usage and state changes, providing a clear audit trail.

Always document the pre-conditions, simulated actions, and expected vs. actual outcomes for each test. Key metrics to record include final contract state, user balances, protocol reserves, and gas costs. This documentation forms the basis for your security report and informs necessary parameter adjustments. Before finalizing any change, re-run the core protocol integration tests on the forked environment to ensure new parameters don't break existing functionality. This rigorous, simulation-driven approach significantly de-risks the deployment of security updates and economic parameter changes.

TRANSACTION SIMULATION

Frequently Asked Questions

Common questions and troubleshooting for developers building or using transaction simulation environments to test smart contract attacks.

A transaction simulation environment is a controlled, isolated sandbox that executes a blockchain transaction against a specific state to predict its outcome without broadcasting it to the live network. For attack testing, it's essential because it allows you to:

  • Safely test exploits like reentrancy, flash loan manipulations, or oracle attacks without risking real funds or causing damage.
  • Validate attack vectors by replaying historical malicious transactions to understand their mechanics.
  • Estimate gas costs for complex attack sequences, which is critical for feasibility.
  • Fork mainnet state using tools like Foundry's cheatcodes or Hardhat Network to test against real-world contract deployments and liquidity conditions.

Without simulation, testing attacks is prohibitively risky and expensive.

conclusion
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

You have now built a foundational transaction simulation environment for attack testing. This guide covered the core components: setting up a local fork, crafting malicious payloads, and analyzing results.

The environment you've implemented provides a sandbox for proactive security. By simulating attacks like front-running, reentrancy, and price oracle manipulation on a forked mainnet, you can identify vulnerabilities before they are exploited in production. This method is significantly more effective than static analysis alone, as it tests contract logic against real-world state and transaction ordering.

To extend your setup, consider integrating with specialized tools. The Foundry framework's forge command offers built-in fuzzing and differential testing. For a more visual and protocol-specific approach, platforms like Tenderly provide simulation APIs and debugging interfaces. Automating attack scenarios with scripts that iterate through parameter variations (e.g., varying slippage tolerance or loan-to-value ratios) will increase your test coverage.

Your next steps should focus on continuous integration. Incorporate transaction simulations into your CI/CD pipeline to test every pull request against a suite of known attack vectors. Monitor and update your fork's state frequently to ensure tests run against current market conditions and contract deployments. Finally, contribute to the ecosystem by sharing reproducible test cases and findings with the project's security community or on platforms like Immunefi.