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:
- Configuration parsing — Reads
Acton.tomlfor wallet entries specifying mnemonic sources (mnemonic,mnemonic_file,mnemonic_env, ormnemonic_keyring) - Wallet instantiation — Calls
load_mnemonicinsrc/wallets.rsto resolve the source and generate deterministic key pairs usingMnemonic::to_key_pair() - Address validation — Constructs a
TonWalletviaTonWallet::new_with_paramsfor the selectedWalletVersion(V1, V2, V5, High-Load, etc.) and validates againstexpected.address_{net}fields - Message building — Creates internal messages with destination addresses, amounts, optional payloads, and flags like
SEND_MODE_PAY_FEES_SEPARATELY - Signing and serialization — Signs the message via
TonWallet::sign_messageand serializes the Cell tree to a base64-encoded BOC usington::boc::serialize - HTTP broadcast — Posts to
https://toncenter.com/api/v2/sendBocviasrc/external_send.rswith 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_envin 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 transactionsenderAddress— 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. Containsopen_wallets,load_mnemonic, and thewallet_idhelper for mappingWalletVersionto 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 thesendsubcommand, 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →