Skip to main content
Version: Devnet (v3.0.0-devnet.20251212)

Testing Smart Contracts

This guide covers how to test Aztec smart contracts by connecting to a local network, deploying contracts, and verifying their behavior.

Prerequisites

Setting up the test environment

Connect to your local Aztec network and create a test wallet:

import { createAztecNodeClient, waitForNode } from "@aztec/aztec.js/node";
import { TestWallet } from "@aztec/test-wallet/server";
import { createLogger } from "@aztec/aztec.js/log";

const AZTEC_NODE_URL = process.env.AZTEC_NODE_URL || "http://localhost:8080";
const logger = createLogger("e2e:token");

// Create client connected to the local network
const node = createAztecNodeClient(AZTEC_NODE_URL);

// Wait for local network to be ready
await waitForNode(node, logger);

// Create a test wallet
const wallet = await TestWallet.create(node);

const nodeInfo = await node.getNodeInfo();
logger.info("Aztec Local Network Info", nodeInfo);

The TestWallet manages accounts, tracks deployed contracts, and handles transaction proving. It connects to the Aztec node which provides access to both the Private eXecution Environment (PXE) and the network.

Loading test accounts

The local network comes with pre-funded accounts. Load them into your wallet:

import { registerInitialLocalNetworkAccountsInWallet } from "@aztec/test-wallet/server";

const [alice, bob] = await registerInitialLocalNetworkAccountsInWallet(wallet);

Deploying contracts in tests

Deploy contracts using the generated contract class:

import { TokenContract } from "@aztec/noir-contracts.js/Token";

const contract = await TokenContract.deploy(
wallet,
alice, // admin
"TestToken",
"TST",
18
)
.send({ from: alice })
.deployed();

Verifying contract state

Use .simulate() to read contract state without creating a transaction:

const balance = await contract.methods
.balance_of_public(newAccountAddress)
.simulate({ from: newAccountAddress });

expect(balance).toEqual(1n);

Simulations are free (no gas cost) and return the function's result directly. Use them for:

  • Checking balances and state before/after transactions
  • Validating expected outcomes in assertions
  • Debugging contract behavior

Sending test transactions

Send transactions and wait for confirmation:

await contract.methods
.transfer(bob, 100n)
.send({ from: alice })
.wait();

The .wait() method blocks until the transaction is included in a block.

Example test structure

Here's a complete test example using Jest:

import { createAztecNodeClient, waitForNode } from "@aztec/aztec.js/node";
import { AztecAddress } from "@aztec/aztec.js/addresses";
import {
TestWallet,
registerInitialLocalNetworkAccountsInWallet,
} from "@aztec/test-wallet/server";
import { TokenContract } from "@aztec/noir-contracts.js/Token";

describe("Token contract", () => {
let wallet: TestWallet;
let alice: AztecAddress;
let bob: AztecAddress;
let token: TokenContract;

beforeAll(async () => {
const node = createAztecNodeClient("http://localhost:8080");
await waitForNode(node);
wallet = await TestWallet.create(node);
[alice, bob] = await registerInitialLocalNetworkAccountsInWallet(wallet);

token = await TokenContract.deploy(wallet, alice, "Test", "TST", 18)
.send({ from: alice })
.deployed();
});

it("mints tokens to an account", async () => {
await token.methods.mint_to_public(alice, 1000n).send({ from: alice }).wait();

const balance = await token.methods
.balance_of_public(alice)
.simulate({ from: alice });

expect(balance).toEqual(1000n);
});

it("transfers tokens between accounts", async () => {
await token.methods.transfer_in_public(bob, 100n).send({ from: alice }).wait();

const aliceBalance = await token.methods
.balance_of_public(alice)
.simulate({ from: alice });
const bobBalance = await token.methods
.balance_of_public(bob)
.simulate({ from: bob });

expect(aliceBalance).toEqual(900n);
expect(bobBalance).toEqual(100n);
});
});

Testing failure cases

Test that invalid operations revert as expected:

it("reverts when transferring more than balance", async () => {
const balance = await token.methods
.balance_of_public(alice)
.simulate({ from: alice });

await expect(
token.methods
.transfer_in_public(bob, balance + 1n)
.simulate({ from: alice })
).rejects.toThrow();
});

Use .simulate() to test reverts without spending gas. The simulation will throw if the transaction would fail onchain.

Further reading