Acton
Testing

Generating Wrappers

Learn how to automatically generate wrappers for Tolk contracts

When working on integration tests, you'll often need wrappers to send messages and call get-methods. To spare you from writing these manually, Acton offers the wrapper command, which automatically generates wrappers for your contracts.

Generating a Wrapper

To generate a wrapper for a contract defined in your Acton.toml, use the wrapper command:

acton wrapper Counter

This will:

  1. Generate a wrapper file (e.g., wrappers/Counter.gen.tolk).
  2. Optionally generate a test stub if you pass the --test flag.

Bootstrapping Tests with --test

Often when you start testing a contract, you need the same setup boilerplate: imports, deploying the contract, and basic checks. The --test flag can generate a ready-to-run test file for you.

acton wrapper Counter --test

This generates tests/Counter.test.tolk with a setupTest() helper and an example test case:

tests/Counter.test.tolk
import "wrappers/Counter.gen"
import "@acton/emulation/testing"
// ... other imports

/// Initializes the test environment, creating a fresh instance of the contract.
/// Returns the contract wrapper and two treasury accounts (`deployer` and `notDeployer`).
fun setupTest() {
    // Create a treasury account for deployment (typically the owner)
    val deployer = testing.treasury("deployer");
    // Create another treasury account for testing interactions from other users
    val notDeployer = testing.treasury("not_deployer");

    // Initialize and deploy the contract with default values
    val contract = Counter.fromStorage({
        id: 0,
        counter: 0,
    });
    val res = contract.deploy(deployer.address, { value: ton("1") });
    expect(res).toHaveSuccessfulDeploy({ to: contract.address });

    return (contract, deployer, notDeployer);
}

/// Example test case demonstrating the basic flow
get fun `test basic flow`() {
    val (contract, deployer, notDeployer) = setupTest();

    // TODO: Implement your test logic here
    // Example:
    // val res = contract.sendMsg(deployer.address, ...);
    // expect(res).toHaveTransaction({ ... });
}

This gets you from zero to a running test in one command!

Requirements and "Gotchas"

Contract Header Is Required

Wrapper generation uses the ABI emitted by the Tolk compiler. The contract header is the source of truth for:

  • storage, via storage: ...
  • incoming message wrappers, via incomingMessages: ...

If incomingMessages is not declared in the contract header, send{MessageName} methods will not be generated. If storage is missing, the wrapper falls back to an empty fromStorage() initializer instead of a typed storage-based one.

contracts/Counter.tolk
contract Counter {
    storage: Storage
    incomingMessages: AllowedMessage
}

struct Storage {
    counter: uint32
}

struct (0x7e8764ef) Increase {
    amount: int32
}

type AllowedMessage = Increase

fun onInternalMessage(in: InMessage) {
    val msg = lazy AllowedMessage.fromSlice(in.body);
    match (msg) {
        Increase => { ... }
    }
}

Types Can Be Local Or Imported

Storage and message types may live either in the contract file itself or in imported files. Both layouts are supported.

contracts/types.tolk
struct (0x7e8764ef) Increase {
    amount: int32
}

type AllowedMessage = Increase
contracts/Counter.tolk
import "types"

contract Counter {
    storage: Storage
    incomingMessages: AllowedMessage
}

struct Storage {
    counter: uint32
}

fun onInternalMessage(in: InMessage) {
    val msg = lazy AllowedMessage.fromSlice(in.body);
    match (msg) {
        Increase => { ... }
    }
}

Contract must be in Acton.toml

The command takes a contract name, not a file path. Ensure your contract is defined in the configuration:

Acton.toml
[contracts.Counter]
display-name = "Counter"
src = "contracts/Counter.tolk"

Using the Generated Wrapper

The generated wrapper provides a struct representing your contract and methods to interact with it.

Initializing and Deploying

The wrapper provides a static fromStorage method to create the initial state and a deploy method to put it on the chain.

tests/Counter.test.tolk
import "@wrappers/Counter"
// Don't forget to import types for the storage struct!
import "@contracts/types"
import "@acton/emulation/testing"

get fun `test example`() {
    // 1. Create contract instance from storage data
    val contract = Counter.fromStorage({
        counter: 0,
    });

    // 2. Deploy it
    val deployer = testing.treasury("deployer");
    contract.deploy(deployer.address, { value: ton("1") });
}

Sending Messages

For every message type listed in contract { incomingMessages: ... }, the wrapper generates a corresponding send{MessageName} method.

If your contract handles Increase:

// Generated method signature:
fun Counter.sendIncrease(self, from: address, amount: int, config: ...): SendResultList

You can use it like this:

// Send the "Increase" message
contract.sendIncrease(deployer.address, 10);

// With custom value and bounce flag
contract.sendIncrease(deployer.address, 10, {
    value: ton("0.5"),
    bounce: true
});

Calling Get Methods

Get-methods are also wrapped. They return typed values directly.

val current = contract.currentCounter();
expect(current).toEqual(10);

Command Options

You can customize the output paths:

# Generate wrapper in a specific file
acton wrapper Counter --output tests/my_wrapper.tolk

# Generate wrapper into a specific directory
acton wrapper Counter --output-dir tests/generated

# Generate a test file alongside the wrapper
acton wrapper Counter --test --test-output tests/my.test.tolk

# Generate a test file into a specific directory
acton wrapper Counter --test --test-output-dir tests/generated

You can also configure project-wide defaults in Acton.toml:

[wrappers.tolk]
output-dir = "tests/generated-wrappers"
generate-test = true
test-output-dir = "tests/generated-tests"

[wrappers.typescript]
output-dir = "./wrappers-ts"

Then:

  • acton wrapper Counter writes tests/generated-wrappers/Counter.gen.tolk and tests/generated-tests/Counter.test.tolk
  • acton wrapper Counter --ts writes wrappers-ts/Counter.gen.ts by default

See the wrapper command reference for more details about CLI options.

Last updated on

On this page