Docs
Testing

Generating wrappers

Learn how to automatically generate wrappers for contracts

Integration tests often need utility wrapper files to interact with smart contracts: send messages and call get-methods. The acton wrapper command generates those wrappers from contract ABI.

For precompiled .boc contracts, add a types interface file first. See Precompiled BoC contracts.

Generate a wrapper

Run acton wrapper with the contract name from Acton.toml:

acton wrapper Counter

This will generate a Tolk wrapper file, e.g., wrappers/Counter.gen.tolk.

Bootstrap tests with --test

New contract tests repeat the same boilerplate: imports, deploy, and basic checks. Pass --test to emit a runnable test stub file in addition to the wrapper:

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(): (Counter, Treasury, Treasury) {
    // 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("notDeployer");

    // Initialize and deploy the contract with default values
    val contract = Counter.fromStorage({
        id: 0,
        owner: deployer.address,
        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({ ... });
}

Requirements and caveats

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 in the contract file or imports

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 => { ... }
    }
}

Register the contract in Acton.toml

The command takes a contract name, not a file path. The contract must appear under [contracts.<Name>] in Acton.toml:

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

Use the generated wrapper

The generated wrapper provides a struct representing the contract and helper methods to deploy and interact with it.

Initialize and deploy

The wrapper provides 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.gen"
// NOTE: Always import types used by 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") });
}

Send messages

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

For instance, if the counter contract handles the Increase message, the method signature would look like this:

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

Usage examples:

// 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
});

Call get methods

Get-method helpers are also generated. They return typed values directly:

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

Command-line options

Customize 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

Customize project defaults in Acton.toml:

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

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

With this configuration:

  • acton wrapper Counter would write tests/generated-wrappers/Counter.gen.tolk and tests/generated-tests/Counter.test.tolk
  • acton wrapper Counter --ts would write to wrappers-ts/Counter.gen.ts (by default)

See the acton wrapper command reference for more details on CLI options.

Last updated on

On this page