Setting up Tenderly Simulator for testing
Testing a transaction on mainnet through eth_call means reading the chain's state, but not seeing what happens inside. Tenderly Simulator goes further: full execution trace, state changes before sending, simulation with balance and contract state overrides. This is a separate tool that fills the gap between local tests and real mainnet.
What Tenderly Simulator can do that Hardhat fork cannot
Hardhat --fork spins up a local EVM with a mainnet fork. This works for unit tests, but requires running a node locally and managing state manually. Tenderly Simulator is an API: you send a transaction without actually sending it and get a full trace back.
Key capabilities:
- Simulation with state overrides (storage overrides, balance overrides)
- Full stack trace with function names (if contract is verified)
- Gas profiling: how much gas each call spent
- Event logs in human-readable format
- Simulation of transaction bundles (multiple transactions sequentially)
Setup via API
Basic simulation through Tenderly API:
const TENDERLY_API = 'https://api.tenderly.co/api/v1';
const headers = {
'X-Access-Key': process.env.TENDERLY_ACCESS_KEY!,
'Content-Type': 'application/json'
};
const response = await fetch(
`${TENDERLY_API}/account/${TENDERLY_USER}/project/${TENDERLY_PROJECT}/simulate`,
{
method: 'POST',
headers,
body: JSON.stringify({
network_id: '1', // mainnet
from: userAddress,
to: contractAddress,
input: encodedCalldata,
gas: 500000,
gas_price: '0',
value: '0',
save: true, // save simulation to dashboard
state_objects: {
// override user balance
[userAddress]: { balance: '0xDE0B6B3A7640000' } // 1 ETH
}
})
}
);
const simulation = await response.json();
console.log('Gas used:', simulation.transaction.gas_used);
console.log('Status:', simulation.transaction.status); // true/false
Virtual TestNets: replacing local fork
Tenderly Virtual TestNets (formerly Tenderly Forks) is a persistent mainnet fork with RPC endpoint. Connect via MetaMask or wagmi as a regular network and test right in the browser with real UI. No need to deploy Anvil locally.
Creating Virtual TestNet via CLI:
tenderly devnet spawn-rpc \
--template mainnet \
--project my-project \
--account my-account
Returns RPC URL. Add to wagmi config:
const virtualMainnet = defineChain({
id: 1,
name: 'Virtual Mainnet',
rpcUrls: {
default: { http: [process.env.TENDERLY_VIRTUAL_TESTNET_RPC!] }
}
});
State resets on request or by schedule — convenient for CI where each test run starts with clean state.
Integration into CI pipeline
In GitHub Actions add step with simulation of critical transactions before deployment:
- name: Simulate deployment transaction
env:
TENDERLY_ACCESS_KEY: ${{ secrets.TENDERLY_ACCESS_KEY }}
run: npx ts-node scripts/simulate-deploy.ts
Script simulates contract deployment, checks that gas doesn't exceed limit, status is success, no unexpected reverts. Failing simulation means failing CI job, no deployment occurs.
Setup takes 1 working day: creating project in Tenderly, API keys, basic simulation script, pipeline integration.







