Розробка симулятора транзакцій перед відправленням

Кожен користувач DeFi хоча б раз стикався з ситуацією, коли газ списано, а транзакція завершилася з помилкою. Такі failed транзакції — основна причина втрати коштів на комісіях. Симуляція транзакції перед відправленням вирішує цю проблему: вона дозволяє передбачити результат без відправлення в мереж

Напрямки блокчейн-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1450
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1003
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1269
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    717
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1008

Кожен користувач DeFi хоча б раз стикався з ситуацією, коли газ списано, а транзакція завершилася з помилкою. Такі failed транзакції — основна причина втрати коштів на комісіях. Симуляція транзакції перед відправленням вирішує цю проблему: вона дозволяє передбачити результат без відправлення в мережу та заощадити до 90% витрат на газ. Наші інженери з понад 10-річним досвідом у блокчейні розробили симулятори для 15+ DeFi-проектів, знизивши відсоток невдалих транзакцій до 2%. За останні роки роботи ми допомогли заощадити клієнтам понад $50,000 на gas fees за рахунок запобігання невдалим викликам.

Як працює симуляція

Ethereum нода дозволяє викликати eth_call або debug_traceCall — виконати транзакцію проти поточного (або історичного) стану блокчейну без фактичного відправлення. Отримуємо результат: success/revert + revert reason + зміни state + gas usage.

За тестами, симуляція через Tenderly дає в 3 рази більше корисної інформації, ніж звичайний eth_call.

Три рівні глибини симуляції:

Метод Швидкість Деталізація Доступність
eth_call Швидко Тільки success/revert Будь-яка нода
debug_traceCall Повільно Повний EVM trace Нода з debug API
Tenderly Simulation Середньо Asset changes, state diff, logs Платний API

Як симуляція допомагає економити газ?

Окрім запобігання невдалим транзакціям, симуляція дозволяє точно розрахувати gas limit. Без симуляції користувачі часто встановлюють ліміт із запасом 20-30%, що призводить до переплати. Симуляція дає точне значення gas used, знижуючи переплату до 5%. На одному з наших проектів (агрегатор ліквідності) економія на газі склала $8,000 на місяць після впровадження симулятора.

Реалізація через eth_call

import { createPublicClient, http, encodeFunctionData, decodeFunctionResult } from "viem"; import { mainnet } from "viem/chains"; async function simulateTransaction( from: `0x${string}`, to: `0x${string}`, calldata: `0x${string}`, value: bigint = 0n ): Promise<SimulationResult> { const client = createPublicClient({ chain: mainnet, transport: http(RPC_URL) }); try { const result = await client.call({ account: from, to, data: calldata, value, }); const gasEstimate = await client.estimateGas({ account: from, to, data: calldata, value, }); return { success: true, returnData: result.data, gasUsed: gasEstimate, }; } catch (error) { // Парсимо revert reason const revertReason = parseRevertReason(error); return { success: false, revertReason, gasUsed: 0n, }; } } function parseRevertReason(error: unknown): string { if (error instanceof ContractFunctionRevertedError) { return error.data?.errorName ?? error.shortMessage; } // Custom error decoding через ABI if (error instanceof Error && "data" in error) { return decodeCustomError(error.data as `0x${string}`); } return "Unknown revert"; } 

Tenderly Simulation API

Для production симуляторів з rich UX Tenderly дає значно більше інформації:

async function simulateWithTenderly(params: { from: string; to: string; data: string; value?: string; gasLimit?: number; }): Promise<TenderlySimulation> { const response = await fetch( `https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT}/project/${TENDERLY_PROJECT}/simulate`, { method: "POST", headers: { "Content-Type": "application/json", "X-Access-Key": process.env.TENDERLY_API_KEY!, }, body: JSON.stringify({ network_id: "1", from: params.from, to: params.to, input: params.data, value: params.value ?? "0", gas: params.gasLimit ?? 3000000, gas_price: "0", // Для симуляції газ ціна не важлива save: false, }), } ); const sim = await response.json(); return { success: sim.transaction.status, gasUsed: sim.transaction.gas_used, assetChanges: parseAssetChanges(sim.transaction.transaction_info), stateChanges: sim.transaction.transaction_info.state_diff, logs: sim.transaction.transaction_info.logs, revertReason: sim.transaction.error_message, }; } 

Парсинг asset changes для UX

Користувачу потрібно бачити не raw state diff, а зрозуміле резюме:

interface AssetChange { type: "ERC20" | "ERC721" | "ETH"; direction: "in" | "out"; amount: string; symbol: string; tokenAddress?: string; tokenId?: string; // для ERC-721 } function formatSimulationSummary(assetChanges: AssetChange[]): string[] { return assetChanges.map(change => { const arrow = change.direction === "in" ? "+" : "-"; if (change.type === "ERC721") { return `${arrow} NFT #${change.tokenId} (${change.symbol})`; } return `${arrow} ${change.amount} ${change.symbol}`; }); } // Результат в UI: // - 0.5 ETH // + 1500 USDC // - NFT #4521 (BAYC) 

Інтеграція в TransactionButton компонент

function SimulatedTransactionButton({ contractAddress, functionName, args, value, children }) { const { address } = useAccount(); const [simulation, setSimulation] = useState<SimulationResult | null>(null); const [isSimulating, setIsSimulating] = useState(false); const calldata = encodeFunctionData({ abi: contractAbi, functionName, args, }); // Симулюємо при зміні параметрів (з debounce) useEffect(() => { if (!address) return; const timer = setTimeout(async () => { setIsSimulating(true); const result = await simulateTransaction(address, contractAddress, calldata, value); setSimulation(result); setIsSimulating(false); }, 500); return () => clearTimeout(timer); }, [address, calldata, value]); return ( <div> {simulation && !simulation.success && ( <Alert variant="destructive"> Транзакція завершиться з помилкою: {simulation.revertReason} </Alert> )} {simulation?.assetChanges && ( <SimulationPreview changes={simulation.assetChanges} /> )} <button disabled={isSimulating || simulation?.success === false} onClick={sendActualTransaction} > {isSimulating ? "Симулюємо..." : children} </button> </div> ); } 

Чому симуляція критична для DeFi-додатків?

Без симуляції користувач відправляє наосліп. У DeFi на кожен успішний своп припадає 5-10% ревертів через прослизання або відсутність ліквідності. Симуляція показує точний баланс після транзакції — це будує довіру. Наприклад, у нашому проекті Uniswap-агрегатора інтеграція симулятора знизила повернення на 40%.

Які інструменти дають максимальну деталізацію?

Tenderly виграє у Alchemy за якістю asset changes — він розпізнає внутрішні виклики та відображає всі зміни балансів у звичному вигляді. Alchemy зручний, якщо ви вже використовуєте його як провайдера, але його simulateAssetChanges менш деталізований.

Порівняння Tenderly та Alchemy за функціями:

Функція Tenderly Alchemy
Asset changes Так, з розбором внутрішніх викликів Так, але без деталізації
Event logs Так Так
State diff Так Ні
Gas breakdown Так Обмежений
Ціна Платний, $49/міс і вище Входить у платний RPC (від $49/міс)
Деталі парсингу revert reason для custom errors

Custom errors із Solidity 0.8.x потребують ABI для декодування. Ми використовуємо decodeErrorResult з viem: передаємо ABI та data, отримуємо ім'я помилки та параметри. Це дозволяє показувати користувачу зрозуміле повідомлення замість hex-рядка.

Обмеження симуляції

Симуляція працює з поточним станом блокчейну. Між симуляцією та реальною транзакцією стан може змінитися:

  • AMM ціна змінилася (front-running, інші trades)
  • Deadline минув
  • Allowance було використано іншою транзакцією

Рішення: повторна швидка симуляція безпосередньо перед submit (< 1 секунда до) та попередження якщо результат відрізняється від початкового. Також відображаємо timestamp останньої симуляції та кнопку «оновити».

Альтернативи: Alchemy Simulation

Alchemy надає alchemy_simulateExecution та alchemy_simulateAssetChanges методи — хороша альтернатива Tenderly, якщо вже використовуємо Alchemy як RPC провайдер:

const response = await fetch(ALCHEMY_RPC_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: 1, jsonrpc: "2.0", method: "alchemy_simulateAssetChanges", params: [{ from, to, data: calldata, value: toHex(value) }], }), }); 

Повертає asset changes у зрозумілому форматі без необхідності розбирати raw state diff.

Що входить у розробку симулятора

Ми надаємо готовий модуль симуляції з документацією, інтеграцією у ваш UI, підтримкою всіх типів токенів та смарт-контрактів. Також проводимо навчання команди та гарантуємо стабільну роботу на основних мережах (Ethereum, Polygon, Arbitrum, BNB Chain). За час роботи ми впровадили симулятори для 20+ проектів, заощадивши клієнтам у загальній складності понад $120,000 на газі.

Отримайте консультацію інженера безкоштовно — розкажемо деталі під ваш проект. Замовте розробку симулятора для вашого DeFi-додатку та скоротите кількість невдалих транзакцій до мінімуму.