How Acton Handles Gas Estimation for TON Smart Contracts

Acton estimates gas consumption through Transaction.getUsedGas() for executed transactions and provides pure standard library functions like calculateGasFee() to project fees for hypothetical gas amounts using VM opcodes.

Acton, the comprehensive testing framework for The Open Network (TON), implements a dual-layer approach to gas estimation for TON smart contracts within the ton-blockchain/acton repository. By combining runtime telemetry from transaction execution with static fee-calculation primitives, the framework enables developers to audit historical consumption and forecast future costs with nanoton precision. This analysis examines the specific source implementations in crates/ton-retrace and the Tolk standard library that power these capabilities.

Retrieving Actual Gas Consumption with Transaction.getUsedGas()

When Acton's emulator or TonCenter API returns a transaction, the framework wraps the raw response in a Transaction object that exposes granular execution metrics. The getUsedGas() method reads directly from the transaction's compute phase data to return the exact amount of gas consumed.

In crates/ton-retrace/src/types.rs, the Transaction struct deserializes fields including gas_used and gas_fees from the underlying TON data. The method accesses these through the ComputeInfo enum, which distinguishes between successful execution and skipped phases.

The ComputeInfo Enum Structure

The ComputeInfo enum defines two primary variants:

  • Skipped: Indicates the compute phase did not run, such as when targeting an undeployed contract
  • Success: Contains gas_used: u64 and gas_fees fields reflecting actual VM consumption

When getUsedGas() encounters the Success variant, it returns the raw gas_used value as reported by the TVM. This integer represents the exact gas units consumed during the contract's execution cycle.

Handling Skipped Compute Phases

If the transaction's compute phase was skipped, calling getUsedGas() triggers an exception. The test suite validates this behavior in tests/integration/test_runner/transaction_load_body_and_load_in_msg_extract_typed_payload_and_endpoints_tests.rs using expectToEndWithExitCode(567).

This mechanism safeguards developers from accidentally reading gas metrics from transactions that never executed code, such as messages sent to undeployed addresses.

Projecting Fees with Standard Library Functions

Beyond inspecting historical transactions, Acton provides pure functions to calculate theoretical fees based on hypothetical gas amounts. These functions reside in crates/tolk-compiler/assets/tolk-stdlib/gas-payments.tolk.

GETGASFEE and GETGASFEESIMPLE Opcodes

The standard library exposes two primary fee-calculation primitives:

  • calculateGasFee(workchain, gasUsed): Invokes the GETGASFEE VM opcode to determine the total fee for a specified gas amount on a specific workchain
  • calculateGasFeeWithoutFlatPrice: Invokes GETGASFEESIMPLE to calculate fees excluding flat-rate components

Both functions are marked with the @pure annotation, meaning they perform no state modifications and can be called safely from any Tolk or Func script to estimate costs before execution.

// gas-payments.tolk (standard library)
@pure
fun calculateGasFee(workchain: int8, gasUsed: int): coins
    asm(gasUsed workchain) "GETGASFEE"
    
// User script
val fee = calculateGasFee(MASTERCHAIN, 150_000); // fee in nanoton-coins

Integration Workflow: From Execution to Fee Calculation

The complete gas estimation flow combines both mechanisms into a coherent pipeline:

  1. Send a message to a smart contract
  2. Receive a Transaction object from the emulator or API
  3. Extract actual consumption via tx.getUsedGas()
  4. Project costs using calculateGasFee(workchain, gasUnits) to receive nanoton-coin values

This separation of concerns allows developers to verify actual resource usage against theoretical models, identifying discrepancies between expected and realized gas consumption.

// In a Tolk/Func test script
val tx = txs.findTransaction<Ping>({ from: sender.address, to: workerAddress })!;
val used_gas: int = tx.getUsedGas();          // ← reads ComputeInfo.Success.gas_used

Validation Through Integration Tests

Acton's test suite validates gas estimation accuracy in tests/integration/test_runner/transaction_load_body_and_load_in_msg_extract_typed_payload_and_endpoints_tests.rs.

The test transaction_get_used_gas_matches_send_result_for_root_and_child_transactions confirms that rootTx.getUsedGas() aligns with the gasUsed field from transaction lists and exceeds the baseline of 255 units for non-trivial operations.

A separate test function, transaction_get_used_gas_reports_skipped_compute_phase_for_undeployed_destination, sends messages to undeployed addresses and verifies the exit code 567 exception, ensuring the framework correctly identifies skipped compute phases.

// In a test that targets an undeployed destination
val tx = txs.findTransaction<Ping>({ from: sender.address, to: undeployed })!;
expectToEndWithExitCode(567); // Acton reports "compute phase skipped"
tx.getUsedGas();               // throws because the phase was skipped

Summary

  • Runtime inspection: Transaction.getUsedGas() extracts gas_used: u64 from ComputeInfo.Success in executed transactions
  • Static calculation: Pure functions calculateGasFee and calculateGasFeeWithoutFlatPrice wrap GETGASFEE and GETGASFEESIMPLE opcodes for hypothetical fee projection
  • Safety mechanisms: The framework throws exit code 567 when attempting to read gas from skipped compute phases targeting undeployed contracts
  • Source locations: Implementation resides in crates/ton-retrace/src/types.rs and crates/tolk-compiler/assets/tolk-stdlib/gas-payments.tolk
  • Test coverage: Integration tests verify both successful gas retrieval and proper error handling for skipped phases at tests/integration/test_runner/transaction_load_body_and_load_in_msg_extract_typed_payload_and_endpoints_tests.rs

Frequently Asked Questions

What happens if I call getUsedGas() on a transaction with a skipped compute phase?

Acton throws an exception with exit code 567. This occurs when the target contract was not deployed or the compute phase was otherwise skipped, preventing invalid gas readings from non-executed code.

How does Acton calculate the actual fee cost from gas units?

The framework maps gas units to nanoton fees using the calculateGasFee(workchain, gasUsed) function, which invokes the GETGASFEE VM opcode. This accounts for workchain-specific pricing parameters and flat-rate components defined in the TON configuration.

Can I use Acton's gas estimation for transactions that haven't been executed yet?

Yes. While getUsedGas() requires an executed transaction, the @pure functions calculateGasFee and calculateGasFeeWithoutFlatPrice can project fees for any hypothetical gas amount without requiring runtime execution or state changes.

Where are the gas estimation primitives defined in the Acton codebase?

Runtime gas data structures reside in crates/ton-retrace/src/types.rs, defining Transaction and ComputeInfo. The fee-calculation standard library is located at crates/tolk-compiler/assets/tolk-stdlib/gas-payments.tolk.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →