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: u64andgas_feesfields 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 theGETGASFEEVM opcode to determine the total fee for a specified gas amount on a specific workchaincalculateGasFeeWithoutFlatPrice: InvokesGETGASFEESIMPLEto 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:
- Send a message to a smart contract
- Receive a
Transactionobject from the emulator or API - Extract actual consumption via
tx.getUsedGas() - 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()extractsgas_used: u64fromComputeInfo.Successin executed transactions - Static calculation: Pure functions
calculateGasFeeandcalculateGasFeeWithoutFlatPricewrapGETGASFEEandGETGASFEESIMPLEopcodes 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.rsandcrates/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →