Skip to main content

Testing Smart Contracts

This guide explains how to effectively test smart contracts on OP_NET using the Unit Test Framework. It covers deploying contracts, interacting with them, and verifying their state changes.


Deploying Contracts in Tests

1. Instantiating Contract Instances

Create a contract instance and configure it with its deployment parameters.

import { OP_20 } from "@btc-vision/unit-test-framework";

const contract = new OP_20({
file: "MyToken", // Contract file name (it should be in the bytecode folder as MyToken.wasm for example)
deployer: Blockchain.generateRandomAddress(), // Deployer address
address: Blockchain.generateRandomAddress(), // Contract address
decimals: 18, // Number of decimals
gasLimit: 100_000_000_000n, // (Optional) Gas limit, default is 100_000_000_000n
});

2. Registering Contracts with the Blockchain

Register the contract instance with the blockchain using Blockchain.register.

import { Blockchain } from "@btc-vision/unit-test-framework";

Blockchain.register(contract); // Register the contract with the blockchain

3. Initializing Contracts with Parameters (Calldata)

You can initialize contracts with specific parameters, typically passed as calldata.

await contract.init(); // Initialize contract
await contract.deployContract(); // Deploy the contract on the blockchain

Interacting with Contracts

1. Calling Contract Methods

Call contract methods by invoking the respective functions on the contract instance.

const receiverAddress = Blockchain.generateRandomAddress();
await contract.mint(receiverAddress, 1000); // Mint tokens to a specific address

2. Passing Parameters and Handling Return Values

Pass arguments to contract methods and handle the return values.

const balance = await contract.balanceOf(receiverAddress);
Blockchain.log(`Balance: ${balance}`);

Testing Contract State Changes

1. Verifying Storage Changes and State Variables

Ensure that the contract's state changes as expected after a method call.

const testAddress = Blockchain.generateRandomAddress();
await contract.mint(testAddress, 1000);
const newBalance = await contract.balanceOfNoDecimals(testAddress);

Assert.expect(newBalance).toEqual(1000); // Verify the balance was updated

Example Test: Deploying and Interacting with a Contract

import { Address } from "@btc-vision/transaction";
import {
Assert,
Blockchain,
OP_20,
opnet,
OPNetUnit,
} from "@btc-vision/unit-test-framework";

const rndAddress = Blockchain.generateRandomAddress();
const receiver: Address = Blockchain.generateRandomAddress();

await opnet("Contract Interaction Tests", async (vm: OPNetUnit) => {
Blockchain.msgSender = receiver;
Blockchain.txOrigin = receiver;

vm.beforeEach(async () => {
await Blockchain.init();
});

vm.afterAll(() => {
Blockchain.dispose();
});

const contract = new OP_20({
file: "MyToken",
deployer: Blockchain.txOrigin,
address: rndAddress,
decimals: 18,
gasLimit: 100_000_000_000n,
});

Blockchain.register(contract);

await vm.it("should mint tokens and update balance", async () => {
const testAddress = Blockchain.generateRandomAddress();
await contract.mint(testAddress, 1000);

const balance = await contract.balanceOfNoDecimals(testAddress);
Assert.expect(balance).toEqual(1000);
});
});

Best Practices

  • Utilize vm.beforeEach and vm.afterEach to reset states between tests.
  • Use Blockchain.log to track contract interactions during tests.
  • Use assertions to verify storage and state changes after each operation.