Docs

Deployment

Learn how to deploy Tolk smart contracts on testnet and mainnet with Acton

There is no dedicated acton deploy command. Instead, contracts are deployed using Tolk scripts. The same deployment script can be used for local emulation and for testnet or mainnet broadcasts.

Local emulation environment in scripts matches that of Acton tests. Each run is isolated, state is not preserved between script executions, transactions are instantaneous, and emulated wallets are used instead of real configured testnet or mainnet wallets. As such, locally emulated deployments are useful for checking contract address derivation, initial state, deployment statuses, and post-deploy getter calls before deploying contracts to a public network.

Without network broadcast, scripts.wallet(name) creates a virtual wallet in the emulator using the same treasury mechanism as Acton tests. The emulator treasury issues the wallet with a balance of 10,000 GRAM. If the name comes from promptWallet(...), emulate mode returns the stable placeholder "emulated-wallet".

When deploying to testnet or mainnet, scripts.wallet() uses a configured deployer wallet or a wallet connected through TON Connect.

Prerequisites

  • A built Acton project with a Tolk contract wrapper and a deployment script.
  • A configured deployer wallet with at least 1 GRAM for testnet and mainnet deployments. Alternatively, pass --tonconnect to use a wallet from an external wallet service through the TON Connect protocol.

Real funds move on public networks

Local runs stay in the emulator. Testnet and mainnet runs send real external messages from the configured wallet or the connected TON Connect wallet.

Use separate wallets for different networks. Validate the deployment script locally before broadcasting it, and treat mainnet deployment as irreversible.

Deployment script

A deployment script usually does at least four things:

  • loads the deployer wallet
  • builds the contract state
  • sends the deployment transaction
  • waits for the first transaction to go through or for the complete trace to form

To wait until the first transaction is found on-chain, use .waitForFirstTransaction(). To await the complete transaction chain, use .waitForTrace(). Both functions return null if Acton cannot fetch the transaction or the trace within the retry budget.

Each retry is a distinct API call when broadcasting to the testnet or mainnet.

The following example deploys a Counter contract:

scripts/deploy.tolk
import "@acton/io"
import "@acton/emulation/network"
import "@acton/emulation/scripts"
import "@acton/build"
import "@acton/prompts"
import "@acton/testing/assert"

import "@wrappers/Counter.gen"
import "@contracts/types"

fun main() {
    val deployer = scripts.wallet(promptWallet("Select a wallet:"));

    val counter = Counter.fromStorage({
        owner: deployer.address,
        id: 123,
        counter: 0,
    });

    println("Deploying counter to {}", counter.address);

    val result = counter.deploy(deployer.address, { value: ton("0.05") });
    val trace = result.waitForTrace();
    if (trace == null) {
      Assert.fail("Could not find the trace, deployment has failed!");
    }

    println("Deployment complete: {}", counter.address);
    println("On-chain counter is {}", counter.currentCounter());
    // Pretty-print trace tree of transactions:
    println(trace);
}

The same pattern works for more complex contracts. Prepare dictionaries, cells, metadata, and contract code inside the script before calling the wrapper's deployment helper.

scripts/deploy.tolk
import "@acton/emulation/scripts"
import "@acton/build"
import "@acton/prompts"
import "@acton/testing/assert"

import "@wrappers/JettonMinter.gen"
import "@contracts/types"

fun main() {
    val deployer = scripts.wallet(promptWallet("Select a wallet:"));

    val content = buildOnchainMetadata({
        name: "ActonJetton",
        symbol: "ACTON",
        description: "It is time to act on TON!",
        image: "https://ton-blockchain.github.io/acton/logo.png",
    });

    val minter = JettonMinter.fromStorage({
        totalSupply: 1_000_000_000,
        adminAddress: deployer.address,
        content: content,
        jettonWalletCode: build("JettonWallet"),
    });

    val trace = minter.deploy(deployer.address, { value: ton("0.05") }).waitForTrace();
    if (trace == null) {
      Assert.fail("Could not find the trace, deployment has failed!");
    }

    println("Jetton deployed at {}", minter.address);
}

In local emulation environments, transactions are instantaneous, and calls to .deploy() and .send() produce a complete trace right away.

Run the deployment script without --net to execute it locally:

acton script scripts/deploy.tolk

If the local run succeeds, proceed to testnet deployment.

Deploy to testnet

Broadcast the deployment script to testnet with --net testnet:

acton script scripts/deploy.tolk --net testnet

In broadcast mode, scripts.wallet() resolves the named wallet from local wallets.toml or global global.wallets.toml wallet configuration. Select the funded testnet deployer wallet when prompted.

Supply the --tonconnect flag to use the wallet connected through TON Connect:

acton script scripts/deploy.tolk --net testnet --tonconnect

API usage

Unlike local emulator runs, testnet and mainnet deployments call TON Center APIs to send external messages with deployment payloads to wallet contracts, call get methods, and confirm transaction and trace statuses.

Set up and provide a TON Center key in the .env file to prevent 429 errors and access higher RPS limits.

Deploy to mainnet

Broadcast to mainnet only after the same script succeeds locally and on testnet:

acton script scripts/deploy.tolk --net mainnet

Select the mainnet deployer wallet when prompted. When the --tonconnect flag is supplied, Acton uses the wallet connected through TON Connect:

acton script scripts/deploy.tolk --net mainnet --tonconnect

Mainnet deployment

Mainnet deployment transfers real value and publishes the contract state permanently. Confirm the deployer wallet, deployment value, and initialization data before sending the transaction.

Last updated on

On this page