How Acton Handles TON Transactions: Wallet Preparation, Message Signing, andBroadcast

Acton processes TON transactions through a three-layer pipeline that loads wallet mnemonics from secure sources, constructs and signs Cell-based messages using TON crates, and broadcasts signed BOC payloads via the Toncenter HTTP API.

Acton is a Rust-based CLI and library for interacting with The Open Network (TON) blockchain. Understanding how Acton handles TON transactions requires examining its tightly-coupled architecture spanning wallet preparation, message construction, and external broadcast layers.

The Three-Layer Transaction Architecture

Acton implements TON transaction handling across three distinct layers defined in the source code:

Layer Responsibility Main Source File
Wallet preparation Loads mnemonics from environment variables, files, or OS keyrings, derives private keys, creates TonWallet instances, and validates derived addresses against expected configuration values. src/wallets.rs
Message construction and signing Transforms high-level send requests into TON message Cell trees, signs them with wallet key pairs, and serializes them into signed BOC (Bag-of-Cells) payloads. Runtime logic in tests/integration/*.rs and TonWallet::sign_message
Broadcast Transmits signed BOCs to TON nodes via the Toncenter HTTP API (/sendBoc) using multipart HTTP POST requests. src/external_send.rs

This separation allows Acton to swap backend providers, support multiple wallet versions, and mock external APIs for testing.

Step-by-Step Transaction Flow

Acton handles TON transactions through a deterministic six-step process:

  1. Configuration parsing — Reads Acton.toml for wallet entries specifying mnemonic sources (mnemonic, mnemonic_file, mnemonic_env, or mnemonic_keyring)
  2. Wallet instantiation — Calls load_mnemonic in src/wallets.rs to resolve the source and generate deterministic key pairs using Mnemonic::to_key_pair()
  3. Address validation — Constructs a TonWallet via TonWallet::new_with_params for the selected WalletVersion (V1, V2, V5, High-Load, etc.) and validates against expected.address_{net} fields
  4. Message building — Creates internal messages with destination addresses, amounts, optional payloads, and flags like SEND_MODE_PAY_FEES_SEPARATELY
  5. Signing and serialization — Signs the message via TonWallet::sign_message and serializes the Cell tree to a base64-encoded BOC using ton::boc::serialize
  6. HTTP broadcast — Posts to https://toncenter.com/api/v2/sendBoc via src/external_send.rs with the signed BOC and sender address

If broadcast = false is configured, Acton returns the BOC string without contacting Toncenter, enabling dry-run modes.

Wallet Preparation and Key Management

The src/wallets.rs file manages all wallet-related operations. Acton supports multiple mnemonic sources for secure key management:

  • Environment variables — Set via mnemonic_env in configuration
  • Local files — Specified via mnemonic_file
  • OS keyring — Integrated via mnemonic_keyring
  • Direct configuration — Embedded via mnemonic (not recommended for production)

The open_wallets function loads all configured wallets for a specific network, while load_mnemonic resolves the appropriate source and returns the 24-word phrase. The system derives the key pair and validates that the computed address matches any expected addresses declared in Acton.toml (see lines 3000–3006 in src/wallets.rs). Address mismatches trigger immediate errors to prevent fund loss.

Message Construction and BOC Serialization

When processing a net.send(sender, msg) call, Acton constructs a TON internal message containing:

  • Destination address — The target TON address
  • Value — Amount in nanoTON
  • Payload — Optional Cell-based data payload
  • Send mode — Flags such as SEND_MODE_PAY_FEES_SEPARATELY

The TonWallet::sign_message method from the ton_wallet crate creates a signed message Cell. Acton then uses ton::boc::serialize to convert the Cell tree into a binary BOC, base64-encoding the result for HTTP transport. This signed BOC represents the complete transaction payload ready for network broadcast.

Broadcasting via the Toncenter HTTP API

The src/external_send.rs file implements the HTTP client for transaction broadcast. Acton constructs a multipart/form-data POST request to https://toncenter.com/api/v2/sendBoc containing:

  • boc — The base64-encoded signed transaction
  • senderAddress — The base64 representation of the sender wallet

The implementation uses reqwest::Client (or an internal HTTP wrapper) to handle the POST request. Successful responses return the transaction hash, while failures surface detailed error messages to the CLI. This design abstracts the HTTP client, making it possible to replace Toncenter with private TON nodes by modifying src/external_send.rs.

Implementation Examples

Sending a Transaction via CLI

The simplest way to handle TON transactions in Acton uses the command-line interface:


# Assume Acton.toml contains a wallet called "my-wallet"

acton send \
  --wallet my-wallet \
  --to UQAB... \
  --value 0.5 \
  --payload "Hello TON"

This command parses flags, loads the wallet via src/wallets.rs, builds and signs the message, and calls external_send::broadcast_boc to transmit the transaction.

Creating a Signed BOC Programmatically

For applications requiring offline transaction signing or custom broadcast logic:

use acton::wallets::{open_wallets, load_mnemonic};
use ton::ton_core::types::TonAddress;
use ton::ton_wallet::TonWallet;
use ton::boc::BocWriter;

fn build_signed_boc(
    wallet_name: &str,
    config: &ActonConfig,
    net: &Network,
    dest: &str,
    value: u128,
) -> anyhow::Result<String> {
    // 1️⃣ Load the wallet (no broadcast => empty map)
    let wallets = open_wallets(config, Some(net), false)?;
    let wallet = wallets.get(wallet_name).expect("wallet not configured");

    // 2️⃣ Create a message cell
    let destination = TonAddress::from_str(dest)?;
    let msg = wallet.wallet.create_internal_message(
        destination,
        value,
        /* payload = */ None,
        /* send_mode = */ SEND_MODE_PAY_FEES_SEPARATELY,
    )?;

    // 3️⃣ Sign it
    let signed = wallet.wallet.sign_message(&msg)?;

    // 4️⃣ Serialize to BOC and base64-encode
    let mut writer = BocWriter::new();
    writer.write_cell(&signed)?;
    let boc_bytes = writer.finalize()?;
    Ok(base64::encode(&boc_bytes))
}

This pattern mirrors the internal logic in external_send.rs before the HTTP broadcast stage.

Testing with Mock Toncenter

Acton includes comprehensive integration tests in tests/integration/verify_tests.rs that verify the complete transaction flow against a mock server:

#[test]
fn test_verify_send_transaction_successfully() {
    let project = build_verify_backend_project("verify-send-success");
    let toncenter = project.toncenter_mock(); // From tests/support/toncenter.rs

    // Trigger the acton send command in the test process

    // The mock captured the request; assert path and payload:
    let captured = toncenter.captured_requests();
    assert_eq!(captured[3].path, "/sendBoc");
    let body: serde_json::Value = serde_json::from_slice(&captured[3].body).unwrap();
    let boc = body["boc"].as_str().expect("BOC field missing");
    assert!(!boc.is_empty(), "sendBoc request must include non-empty boc");
}

The tests/support/toncenter.rs mock records request paths and payloads, allowing tests to assert that correctly-signed BOCs are sent without hitting the live network.

Key Source Files for Transaction Handling

Understanding Acton TON transaction handling requires familiarity with these specific files:

  • src/wallets.rs — Implements wallet loading, mnemonic resolution, key derivation, and address validation. Contains open_wallets, load_mnemonic, and the wallet_id helper for mapping WalletVersion to contract IDs.
  • src/external_send.rs — Handles HTTP client construction, multipart form building, and POST requests to /sendBoc. Manages response parsing and error handling.
  • src/lib.rs — Entry point that wires CLI commands including the send subcommand, delegating to wallet and external-send layers.
  • tests/support/toncenter.rs — Mock Toncenter server for integration testing that records request metadata and returns canned responses.
  • tests/integration/verify_tests.rs — End-to-end tests demonstrating full transaction lifecycle from configuration to broadcast.

Summary

Acton handles TON transactions through a secure, testable pipeline:

  • Multi-source key management supports environment variables, files, and OS keyrings via src/wallets.rs
  • Type-safe message construction uses TON crates to build Cell trees with configurable send modes and payloads
  • Deterministic signing produces standard BOC payloads compatible with any TON wallet version
  • Abstracted broadcast layer enables both live Toncenter broadcasts and dry-run BOC generation
  • Comprehensive test coverage includes mock HTTP servers to verify signed payload correctness

Frequently Asked Questions

How does Acton secure wallet mnemonics?

Acton stores mnemonics outside source code by supporting environment variables (mnemonic_env), file paths (mnemonic_file), and OS-native keyrings (mnemonic_keyring). The load_mnemonic function in src/wallets.rs resolves these sources at runtime, ensuring private keys never hardcode into configuration files committed to version control.

Can Acton handle different TON wallet versions?

Yes. Acton supports multiple wallet versions including V1, V2, V5, and High-Load wallets through the WalletVersion enum. The wallet_id helper function maps these versions to correct contract IDs for mainnet and testnet deployments. Validation logic in src/wallets.rs ensures the derived address matches expected addresses configured in Acton.toml.

What is the difference between broadcast and non-broadcast modes?

When broadcast = true (default), Acton transmits the signed BOC immediately to Toncenter's /sendBoc endpoint. When broadcast = false, Acton returns the base64-encoded BOC string without network transmission, enabling offline transaction preparation, manual verification, or submission through alternative methods.

How does Acton verify transactions during testing?

Acton uses a mock Toncenter server defined in tests/support/toncenter.rs that records HTTP requests to /sendBoc. Integration tests in tests/integration/verify_tests.rs trigger transaction commands and assert that the captured requests contain valid, non-empty BOC payloads, ensuring the signing and serialization logic produces correctly formatted transactions before mainnet deployment.

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 →