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
andvm.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.