Interacting with the real blockchain
Learn how to use scripts to deploy contracts and send transactions on testnet and mainnet
Acton scripts can interact with the real TON blockchain, enabling contract deployment, transaction sending, and state querying. By using --net <network>, your scripts can execute real operations using configured wallets.
Prerequisites
Before interacting with the real blockchain, you need to configure a wallet in your wallets.toml (or global configuration). Follow the Setting up wallets guide to configure your wallet with a mnemonic.
The --net Flag
Scripts in Acton can run in two modes:
- Local mode (default): Uses a local blockchain emulator with test wallets
- Broadcast mode (
--net <network>): Connects to the real blockchain and uses configured wallets
# Test locally without spending real TON
acton script deploy.tolk
# Deploy to testnet using real wallet
acton script deploy.tolk --net testnet
# Deploy to mainnet using real wallet
acton script deploy.tolk --net mainnetAlways test your scripts without --net first. The local emulator allows you to verify logic before spending real TON on testnet or mainnet.
Manually Controlling Broadcasting Mode
For advanced use cases, you can additionally manually control the broadcasting flag using net.enableBroadcast() and net.disableBroadcast():
fun main() {
net.disableBroadcast();
// ...
// This send will be emulated and not sent to the real blockchain
val result = net.send(wallet.address, msg);
result.wait();
// Enable broadcasting mode again
net.enableBroadcast();
// ...
}Deploying a Contract
Here's a complete example of deploying a counter contract to the blockchain:
import "@acton/io"
import "@acton/emulation/network"
import "@acton/emulation/scripts"
import "@acton/build"
import "@contracts/types"
import "@wrappers/Counter"
fun main() {
val deployer = scripts.wallet("deployer");
val counter = Counter.fromStorage({
id: 8,
counter: 0,
});
val result = counter.deploy(deployer.address, ton("0.05"));
if (!result.wait()) {
return;
}
println("Deployed counter to {}", counter.address);
println("On-chain counter is {}", counter.currentCounter());
}The wrappers/Counter.tolk file provides a convenient wrapper around the counter contract:
// todo update snippet to actual wrapper
import "@acton/build"
import "@acton/emulation/network"
import "@contracts/types"
struct Counter {
address: address
init: ContractState
}
fun Counter.fromStorage(storage: Storage): Counter {
val init = ContractState {
code: build("Counter"),
data: storage.toCell(),
};
val address = AutoDeployAddress { stateInit: init }.calculateAddress();
return Counter { address, init };
}
fun Counter.deploy(self, deployer: address, value: coins) {
val msg = createMessage({
bounce: false,
value: value,
dest: {
stateInit: self.init,
},
});
return net.send(deployer, msg);
}
fun Counter.sendIncrease(self, from: address, increaseBy: int) {
val msg = createMessage({
bounce: false,
value: ton("0.05"),
dest: self.address,
body: IncreaseCounter { increaseBy },
});
return net.send(from, msg);
}
fun Counter.getCounter(self): int {
return net.runGetMethod(self.address, "currentCounter");
}Notice that the wrapper uses the same patterns as in tests, making it easy to reuse testing code in scripts.
Deploy to testnet:
acton script deploy.tolk --net testnetDeploy to mainnet:
acton script deploy.tolk --net mainnetIn broadcast mode, post-deploy getter calls such as
counter.currentCounter() automatically read from the selected broadcast
network. You do not need a separate --fork-net for the common deploy-and-check
flow.
Using Wallets in Scripts
The scripts.wallet() function returns a wallet struct with an address field:
import "@acton/emulation/scripts"
fun main() {
val deployer = scripts.wallet("deployer");
println("Wallet address: {}", deployer.address);
}Behavior:
- Without
--net: Creates a local test wallet (similar totesting.treasury()) - With
--net: Uses the real wallet configured inwallets.tomlor global wallets
This dual behavior allows you to test scripts locally before running them on the real blockchain.
Sending Messages
Use net.send() to send messages from a wallet address:
import "@acton/emulation/scripts"
fun main() {
val deployer = scripts.wallet("deployer");
val msg = createMessage({
bounce: true,
value: ton("0.1"),
dest: someContractAddress,
body: SomeMessage { param: 42 },
});
net.send(deployer.address, msg);
println("Message sent!");
}Be extremely careful with --net. While local tests allow arbitrary big values, sending messages with large values to the real blockchain will spend real TON.
Recommendation: Use small values like ton("0.05") or ton("0.1") for most operations.
Waiting for Transactions
When broadcasting transactions to the real network, you may want to wait for them to be processed. The SendResultList.wait() method polls the network until the transaction is confirmed:
import "@acton/emulation/scripts"
fun main() {
val deployer = scripts.wallet("deployer");
// Deploy counter
val counter = Counter.fromStorage({
id: 8,
counter: 0,
});
val deployResult = counter.deploy(deployer.address, ton("0.05"));
deployResult.wait(); // Wait for deployment
println("Counter deployed at {}", counter.address);
// Send increase message
val increaseResult = counter.sendIncrease(deployer.address, 5);
increaseResult.wait(); // Wait for transaction
println("Counter increased!");
}You can also check the result of waiting:
fun main() {
// ...
val result = net.send(deployer.address, msg);
val success = result.wait();
if (success) {
println("Transaction confirmed!");
} else {
println("Transaction not found after max attempts");
}
}Behavior in Different Modes
The wait() method automatically adapts to the execution mode:
| Mode | Behavior |
|---|---|
| Local (default) | Returns true immediately (no waiting needed) |
--net <network> | Polls the network until transaction is found |
You can check the current mode using net.isBroadcasting():
if (net.isBroadcasting()) {
println("Running on real network");
} else {
println("Running in emulation mode");
}Querying Blockchain State
Scripts can read contract state using net.runGetMethod():
fun main() {
val counterAddress = address("EQDt7LL...");
val currentValue = net.runGetMethod<int>(counterAddress, "currentCounter");
println("Counter value: {}", currentValue);
}Local vs Remote State
By default, net.runGetMethod() queries the local emulator state when the
script runs without --net. To query real deployed contracts without
broadcasting, use the --fork-net flag:
# Query a contract on testnet
acton script query.tolk --fork-net testnet
# Query a contract on mainnet
acton script query.tolk --fork-net mainnet
# With a TonCenter API key to avoid rate limiting
TONCENTER_TESTNET_API_KEY=YOUR_API_KEY acton script query.tolk --fork-net testnetWhen --fork-net is enabled, Acton automatically fetches account state from
the real blockchain and caches it locally for the script execution. This allows
you to:
- Query real contract state
- Test interactions with deployed contracts
- Verify contract behavior against live data
When the script runs with --net, the selected network already becomes
the default remote read network. This means deploy scripts can send a
transaction, wait for it, and immediately call net.runGetMethod() or wrapper
getters against the same chain without passing --fork-net.
Learn more about fork testing in the Fork Testing article.
Combining with --net
--fork-net and --net can still be combined, but in broadcast mode
they must point to the same network.
| Flags | Blockchain State | Wallets | Use Case |
|---|---|---|---|
| None | Local emulator | Test wallets | Local development |
--fork-net testnet | Real testnet state | Test wallets | Query real contracts locally |
--net testnet | Real testnet state | Real wallets | Deploy and read back live state on testnet |
--net mainnet | Real mainnet state | Real wallets | Mainnet deployment and post-deploy verification |
--net testnet --fork-net testnet | Real testnet state | Real wallets | Explicit same-network fork setup, optionally with block pinning |
Passing different --net and --fork-net values in broadcast mode is
rejected to avoid reading from one network while sending to another.
Network Selection
Acton scripts support testnet, mainnet, and localnet:
# Testnet
acton script deploy.tolk --net testnet
# Mainnet
acton script deploy.tolk --net mainnet
# Localnet
acton script deploy.tolk --net localnetLocal Testing vs Broadcasting
It's crucial to understand the difference:
| Aspect | Local Mode | Broadcast Mode |
|---|---|---|
| Blockchain | Local emulator | Real blockchain |
| Wallets | Generated test wallets | Configured real wallets |
| TON spent | None (simulated) | Real TON |
| Transaction speed | Instant | Real network timing |
| State persistence | Lost after script ends | Permanent on blockchain |
Next Steps
- Check the script command reference for all available options
- Learn more about wallet configuration
- Explore verifying your deployed contracts
Last updated on