Пакетные симуляции через RPC
Node RPC позволяет читать данные из блокчейна и отправлять транзакции. Но вы также можете симулировать пакетные транзакции в Node через один RPC URL. Симуляции через RPC доступны только в поддерживаемых сетях. Если ваша сеть не поддерживается в Node, вместо этого запускайте пакетные симуляции через Simulation API. Получите RPC URL из Dashboard. Перейдите в Node > Copy HTTPS URL нужной вам сети.example
https://mainnet.gateway.tenderly.co/$TENDERLY_NODE_ACCESS_KEY
tenderly_simulateBundle. Этот пользовательский метод позволяет симулировать несколько транзакций, передавая их в виде массива. См. справочник RPC.
Транзакции симулируются одна за другой в одном блоке.
- 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
Используйте эндпоинт APIsimulate-bundle, чтобы симулировать несколько последовательных транзакций одновременно. См. справочник API.
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();
Изучите сценарии использования
- Изменения активов и балансов: получайте точные долларовые значения для всех изменений балансов и активов.
- Оценка газа: точно предсказывайте затраты на газ до отправки транзакции.
- Переопределения состояния: изменяйте условия блокчейна, такие как временные метки и данные контракта, для тестирования различных сценариев.
- Предпросмотр результатов транзакции: выявляйте и устраняйте проблемы, которые могут привести к сбою транзакций.