RPC bundled simulations
Node RPC allows you to read blockchain data and send transactions. But you can also simulate bundled transactions on the Node through a single RPC URL. Simulations via RPC are only available on supported networks. If your network is not supported on Node, run bundled simulations via Simulation API instead. Retrieve the RPC URL from the Dashboard. Go to Node > Copy HTTPS URL of the desired network.example
https://mainnet.gateway.tenderly.co/$TENDERLY_NODE_ACCESS_KEY
tenderly_simulateBundle method. This custom method allows you to simulate several transactions by passing them as an array. See RPC reference.
Transactions are simulated one after the other in the same block.
- example
- response.json
example
curl https://mainnet.gateway.tenderly.co/$TENDERLY_NODE_ACCESS_KEY \
-X POST \
-H "Content-Type: application/json" \
-d \
'{
"id": 0,
"jsonrpc": "2.0",
"method": "tenderly_simulateBundle",
"params": [
[
{
"from": "0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"data": "0x40c10f19000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce10000000000000000000000000000000000000000000000001bc16d674ec80000"
},
{
"from": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"gas": "0x7a1200",
"data": "0x095ea7b3000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd0000000000000000000000000000000000000000000000000de0b6b3a7640000"
},
{
"from": "0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"gas": "0x7a1200",
"data": "0x23b872dd000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570000000000000000000000000000000000000000000000000006a94d74f430000"
}
],
"latest",
{
"0x6b175474e89094c44da98b954eedeac495271d0f": {
"stateDiff": {
"0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
}
}
]
}'
response.json
{
"id": 0,
"jsonrpc": "2.0",
"result": [
{
"status": true,
"gasUsed": "0xd0d9",
"cumulativeGasUsed": "0x0",
"blockNumber": "0x121c950",
"type": "0x0",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000010000000020000000000000000000800000000000000000000000010000000000000000000004000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000020000000002000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000",
"logs": [
{
"name": "Transfer",
"anonymous": false,
"inputs": [
{
"value": "0x0000000000000000000000000000000000000000",
"type": "address",
"name": "src"
},
{
"value": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"type": "address",
"name": "dst"
},
{
"value": "2000000000000000000",
"type": "uint256",
"name": "wad"
}
],
"raw": {
"address": "0x6b175474e89094c44da98b954eedeac495271d0f",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1"
],
"data": "0x0000000000000000000000000000000000000000000000001bc16d674ec80000"
}
}
],
"trace": [
{
"type": "CALL",
"from": "0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"gas": "0xbeb6d8c",
"gasUsed": "0x7c65",
"value": "0x0",
"input": "0x40c10f19000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce10000000000000000000000000000000000000000000000001bc16d674ec80000",
"decodedInput": [
{
"value": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"type": "address",
"name": "usr"
},
{
"value": "2000000000000000000",
"type": "uint256",
"name": "wad"
}
],
"method": "mint",
"output": "0x",
"subtraces": 0,
"traceAddress": []
}
],
"assetChanges": [
{
"assetInfo": {
"standard": "ERC20",
"type": "Fungible",
"contractAddress": "0x6b175474e89094c44da98b954eedeac495271d0f",
"symbol": "dai",
"name": "Dai",
"logo": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.webp?1696509996",
"decimals": 18,
"dollarValue": "0.9996880292892456"
},
"type": "Mint",
"to": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"rawAmount": "0x1bc16d674ec80000",
"amount": "2",
"dollarValue": "1.9993760585784912109"
}
],
"balanceChanges": [
{
"address": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"dollarValue": "1.9993760585784912109",
"transfers": [
0
]
}
]
},
{
"status": true,
"gasUsed": "0xb442",
"cumulativeGasUsed": "0x0",
"blockNumber": "0x121c950",
"type": "0x0",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000200000000000000000000000000008000000000000000000000000000000000000000010000000000000000000020000000000000000000000000000000000000000000000000000004000800000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000002000000000000000000000000100010000000000000000000000000000000000000000000000000000000000000",
"logs": [
{
"name": "Approval",
"anonymous": false,
"inputs": [
{
"value": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"type": "address",
"name": "src"
},
{
"value": "0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd",
"type": "address",
"name": "guy"
},
{
"value": "1000000000000000000",
"type": "uint256",
"name": "wad"
}
],
"raw": {
"address": "0x6b175474e89094c44da98b954eedeac495271d0f",
"topics": [
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"0x000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1",
"0x000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd"
],
"data": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
}
}
],
"trace": [
{
"type": "CALL",
"from": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"gas": "0x79bd80",
"gasUsed": "0x5fc2",
"value": "0x0",
"input": "0x095ea7b3000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd0000000000000000000000000000000000000000000000000de0b6b3a7640000",
"decodedInput": [
{
"value": "0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd",
"type": "address",
"name": "usr"
},
{
"value": "1000000000000000000",
"type": "uint256",
"name": "wad"
}
],
"method": "approve",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"decodedOutput": [
{
"value": true,
"type": "bool",
"name": ""
}
],
"subtraces": 0,
"traceAddress": []
}
]
},
{
"status": true,
"gasUsed": "0xe38b",
"cumulativeGasUsed": "0x0",
"blockNumber": "0x121c950",
"type": "0x0",
"logsBloom": "0x00000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000010000000000000000000004000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000002000000000000000000020000000002000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000",
"logs": [
{
"name": "Transfer",
"anonymous": false,
"inputs": [
{
"value": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"type": "address",
"name": "src"
},
{
"value": "0xbd8daa414fda8a8a129f7035e7496759c5af8570",
"type": "address",
"name": "dst"
},
{
"value": "30000000000000000",
"type": "uint256",
"name": "wad"
}
],
"raw": {
"address": "0x6b175474e89094c44da98b954eedeac495271d0f",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1",
"0x000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570"
],
"data": "0x000000000000000000000000000000000000000000000000006a94d74f430000"
}
}
],
"trace": [
{
"type": "CALL",
"from": "0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"gas": "0x79bc28",
"gasUsed": "0x8db3",
"value": "0x0",
"input": "0x23b872dd000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570000000000000000000000000000000000000000000000000006a94d74f430000",
"decodedInput": [
{
"value": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"type": "address",
"name": "src"
},
{
"value": "0xbd8daa414fda8a8a129f7035e7496759c5af8570",
"type": "address",
"name": "dst"
},
{
"value": "30000000000000000",
"type": "uint256",
"name": "wad"
}
],
"method": "transferFrom",
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
"decodedOutput": [
{
"value": true,
"type": "bool",
"name": ""
}
],
"subtraces": 0,
"traceAddress": []
}
],
"assetChanges": [
{
"assetInfo": {
"standard": "ERC20",
"type": "Fungible",
"contractAddress": "0x6b175474e89094c44da98b954eedeac495271d0f",
"symbol": "dai",
"name": "Dai",
"logo": "https://assets.coingecko.com/coins/images/9956/large/Badge_Dai.webp?1696509996",
"decimals": 18,
"dollarValue": "0.9996880292892456"
},
"type": "Transfer",
"from": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"to": "0xbd8daa414fda8a8a129f7035e7496759c5af8570",
"rawAmount": "0x6a94d74f430000",
"amount": "0.03",
"dollarValue": "0.029990640878677368163"
}
],
"balanceChanges": [
{
"address": "0xbd8daa414fda8a8a129f7035e7496759c5af8570",
"dollarValue": "0.029990640878677368163",
"transfers": [
0
]
},
{
"address": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"dollarValue": "-0.029990640878677368163",
"transfers": [
0
]
}
]
}
]
}
API bundled simulations
Use thesimulate-bundle API endpoint to simulate multiple consecutive transactions at once. See API reference.
example
https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/simulate-bundle
- Shell
- request.ts
curl --request POST \
--url https://api.tenderly.co/api/v1/account/me/project/project/simulate-bundle \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header "X-Access-Key: $TENDERLY_ACCESS_KEY" \
--data '{
"simulations": [
{
"network_id": "1",
"save": true,
"save_if_fails": true,
"simulation_type": "full",
"from": "0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"input": "0x40c10f19000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce10000000000000000000000000000000000000000000000001bc16d674ec80000",
"state_objects": {
"0x6b175474e89094c44da98b954eedeac495271d0f": {
"storage": {
"0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03": "0x0000000000000000000000000000000000000000000000000000000000000001"
}
}
}
},
{
"network_id": "1",
"save": true,
"save_if_fails": true,
"simulation_type": "full",
"from": "0xe58b9ee93700a616b50509c8292977fa7a0f8ce1",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"input": "0x095ea7b3000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd0000000000000000000000000000000000000000000000000de0b6b3a7640000"
},
{
"network_id": "1",
"save": true,
"save_if_fails": true,
"simulation_type": "full",
"from": "0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"input": "0x23b872dd000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570000000000000000000000000000000000000000000000000006a94d74f430000"
}
]
}'
request.ts
dotenv.config();
// assuming environment variables TENDERLY_ACCOUNT_SLUG, TENDERLY_PROJECT_SLUG and TENDERLY_ACCESS_KEY are set
const { TENDERLY_ACCOUNT_SLUG, TENDERLY_PROJECT_SLUG, TENDERLY_ACCESS_KEY } = process.env;
const batchedSimulations = async () => {
console.time('Batch Simulation');
const daiSequence = (
await axios.post(
`https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_SLUG}/project/${TENDERLY_PROJECT_SLUG}/simulate-bundle`,
// the transaction
{
simulations: getTxSequence().map((transaction) => ({
network_id: '1', // network to simulate on
save: true,
save_if_fails: true,
simulation_type: 'full',
...transaction,
})),
},
{
headers: {
'X-Access-Key': TENDERLY_ACCESS_KEY as string,
},
},
)
).data;
console.timeEnd('Batch Simulation');
console.log(JSON.stringify(daiSequence, null, 2));
};
function getTxSequence() {
const fakeWardAddress = '0xe2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2';
const daiOwner = 'e58b9ee93700a616b50509c8292977fa7a0f8ce1';
const daiSpend = 'f7ddedc66b1d482e5c38e4730b3357d32411e5dd';
const daiRecip = 'e58b9ee93700a616b50509c8292977fa7a0f8ce1';
const daiContract = '0x6b175474e89094c44da98b954eedeac495271d0f';
return [
// TX1: Mint 2 DAI for e58b9ee93700a616b50509c8292977fa7a0f8ce1.
// Must do state override so sender (from) is considered a ward for this simulation
{
from: fakeWardAddress,
to: '0x6b175474e89094c44da98b954eedeac495271d0f',
input:
'0x40c10f19000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce10000000000000000000000000000000000000000000000001bc16d674ec80000',
// make 0xcace477c66b1f2e151974eeada9ac8b95a31c50593ae1db2f048f0a2b54c1424 a ward
state_objects: {
'0x6b175474e89094c44da98b954eedeac495271d0f': {
storage: {
'0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03':
'0x0000000000000000000000000000000000000000000000000000000000000001',
},
},
},
},
// TX2: e58b9ee93700a616b50509c8292977fa7a0f8ce1 approves 1 DAI to f7ddedc66b1d482e5c38e4730b3357d32411e5dd
{
from: '0xe58b9ee93700a616b50509c8292977fa7a0f8ce1',
to: '0x6b175474e89094c44da98b954eedeac495271d0f',
input:
'0x095ea7b3000000000000000000000000f7ddedc66b1d482e5c38e4730b3357d32411e5dd0000000000000000000000000000000000000000000000000de0b6b3a7640000',
},
{
// TX3: 0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd calls transferFrom to transfer 0.5 DAI belonging to e58b9ee93700a616b50509c8292977fa7a0f8ce1, sending them to e58b9ee93700a616b50509c8292977fa7a0f8ce1
from: '0xf7ddedc66b1d482e5c38e4730b3357d32411e5dd',
to: '0x6b175474e89094c44da98b954eedeac495271d0f',
input:
'0x23b872dd000000000000000000000000e58b9ee93700a616b50509c8292977fa7a0f8ce1000000000000000000000000bd8daa414fda8a8a129f7035e7496759c5af8570000000000000000000000000000000000000000000000000006a94d74f430000',
},
];
}
function getWardStorageLocation(wardAddress: string) {
// calculate the storage location of `wards[wardAddress]`
// yields 0xedd7d04419e9c48ceb6055956cbb4e2091ae310313a4d1fa7cbcfe7561616e03
return ethers.utils.keccak256(
ethers.utils.concat([
ethers.utils.hexZeroPad(wardAddress, 32), // the ward address (address 0x000..0) - mapping key
ethers.utils.hexZeroPad('0x0', 32), // the wards slot is 0th in the DAI contract - the mapping variable
]),
);
}
batchedSimulations();
Explore use cases
- Asset and balance changes: Get exact dollar values for all balance and asset changes that will happen.
- Gas estimation: Accurately predict the gas costs before sending the transaction.
- State overrides: Modify blockchain conditions like timestamps and contract data to test different scenarios.
- Preview transaction outcomes: Identify and fix issues that could cause transactions to fail.