Skip to content

AVM Opcodes

The coverage file provides a comprehensive list of all opcodes and their respective types, categorized as Mockable, Emulated, or Native within the algorand-typescript-testing package. This section highlights a subset of opcodes and types that typically require interaction with the test execution context.

Native opcodes are assumed to function as they do in the Algorand Virtual Machine, given their stateless nature. If you encounter issues with any Native opcodes, please raise an issue in the algorand-typescript-testing repo or contribute a PR following the Contributing guide.

import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
// Create the context manager for snippets below
const ctx = new TestExecutionContext();

Implemented Types

These types are fully implemented in TypeScript and behave identically to their AVM counterparts:

1. Cryptographic Operations

The following opcodes are demonstrated:

  • op.sha256
  • op.keccak256
  • op.ecdsaVerify
import { op } from '@algorandfoundation/algorand-typescript';
// SHA256 hash
const data = Bytes('Hello, World!');
const hashed = op.sha256(data);
// Keccak256 hash
const keccakHashed = op.keccak256(data);
// ECDSA verification
const messageHash = Bytes.fromHex(
'f809fd0aa0bb0f20b354c6b2f86ea751957a4e262a546bd716f34f69b9516ae1',
);
const sigR = Bytes.fromHex('18d96c7cda4bc14d06277534681ded8a94828eb731d8b842e0da8105408c83cf');
const sigS = Bytes.fromHex('7d33c61acf39cbb7a1d51c7126f1718116179adebd31618c4604a1f03b5c274a');
const pubkeyX = Bytes.fromHex('f8140e3b2b92f7cbdc8196bc6baa9ce86cf15c18e8ad0145d50824e6fa890264');
const pubkeyY = Bytes.fromHex('bd437b75d6f1db67155a95a0da4b41f2b6b3dc5d42f7db56238449e404a6c0a3');
const result = op.ecdsaVerify(op.Ecdsa.Secp256r1, messageHash, sigR, sigS, pubkeyX, pubkeyY);
expect(result).toBe(true);

2. Arithmetic and Bitwise Operations

The following opcodes are demonstrated:

  • op.addw
  • op.bitLength
  • op.getBit
  • op.setBit
import { op, Uint64 } from '@algorandfoundation/algorand-typescript';
// Addition with carry
const [result, carry] = op.addw(Uint64(2n ** 63n), Uint64(2n ** 63n));
// Bitwise operations
const value = Uint64(42);
const bitLength = op.bitLength(value);
const isBitSet = op.getBit(value, 3);
const newValue = op.setBit(value, 2, 1);

For a comprehensive list of all opcodes and types, refer to the coverage page.

Emulated Types Requiring Transaction Context

These types necessitate interaction with the transaction context:

algopy.op.Global

import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
import { op, arc4, uint64, Uint64 } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class MyContract extends arc4.Contract {
@arc4.abimethod()
checkGlobals(): uint64 {
return op.Global.minTxnFee + op.Global.minBalance;
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
ctx.ledger.patchGlobalData({
minTxnFee: 1000,
minBalance: 100000,
});
const contract = ctx.contract.create(MyContract);
const result = contract.checkGlobals();
expect(result).toEqual(101000);

algopy.op.Txn

import { op, arc4 } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class MyContract extends arc4.Contract {
@arc4.abimethod()
checkTxnFields(): arc4.Address {
return new arc4.Address(op.Txn.sender);
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(MyContract);
const customSender = ctx.any.account();
ctx.txn.createScope([ctx.any.txn.applicationCall({ sender: customSender })]).execute(() => {
const result = contract.checkTxnFields();
expect(result).toEqual(customSender);
});

algopy.op.AssetHoldingGet

import { Account, arc4, Asset, op, uint64, Uint64 } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class AssetContract extends arc4.Contract {
@arc4.abimethod()
checkAssetHolding(account: Account, asset: Asset): uint64 {
const [balance, _] = op.AssetHolding.assetBalance(account, asset);
return balance;
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(AssetContract);
const asset = ctx.any.asset({ total: 1000000 });
const account = ctx.any.account({ optedAssetBalances: new Map([[asset.id, Uint64(5000)]]) });
const result = contract.checkAssetHolding(account, asset);
expect(result).toEqual(5000);

algopy.op.AppGlobal

import { arc4, bytes, Bytes, op, uint64, Uint64 } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class StateContract extends arc4.Contract {
@arc4.abimethod()
setAndGetState(key: bytes, value: uint64): uint64 {
op.AppGlobal.put(key, value);
return op.AppGlobal.getUint64(key);
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(StateContract);
const key = Bytes('test_key');
const value = Uint64(42);
const result = contract.setAndGetState(key, value);
expect(result).toEqual(value);
const [storedValue, _] = ctx.ledger.getGlobalState(contract, key);
expect(storedValue?.value).toEqual(42);

algopy.op.Block

import { arc4, bytes, op } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class BlockInfoContract extends arc4.Contract {
@arc4.abimethod()
getBlockSeed(): bytes {
return op.Block.blkSeed(1000);
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(BlockInfoContract);
ctx.ledger.patchBlockData(1000, { seed: op.itob(123456), timestamp: 1625097600 });
const seed = contract.getBlockSeed();
expect(seed).toEqual(op.itob(123456));

algopy.op.AcctParamsGet

import type { Account, uint64 } from '@algorandfoundation/algorand-typescript';
import { arc4, assert, op, Uint64 } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class AccountParamsContract extends arc4.Contract {
@arc4.abimethod()
getAccountBalance(account: Account): uint64 {
const [balance, exists] = op.AcctParams.acctBalance(account);
assert(exists);
return balance;
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(AccountParamsContract);
const account = ctx.any.account({ balance: 1000000 });
const balance = contract.getAccountBalance(account);
expect(balance).toEqual(Uint64(1000000));

algopy.op.AppParamsGet

import type { Application } from '@algorandfoundation/algorand-typescript';
import { arc4, assert, op } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class AppParamsContract extends arc4.Contract {
@arc4.abimethod()
getAppCreator(appId: Application): arc4.Address {
const [creator, exists] = op.AppParams.appCreator(appId);
assert(exists);
return new arc4.Address(creator);
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(AppParamsContract);
const app = ctx.any.application();
const creator = contract.getAppCreator(app);
expect(creator).toEqual(ctx.defaultSender);

algopy.op.AssetParamsGet

import type { uint64 } from '@algorandfoundation/algorand-typescript';
import { arc4, assert, op } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class AssetParamsContract extends arc4.Contract {
@arc4.abimethod()
getAssetTotal(assetId: uint64): uint64 {
const [total, exists] = op.AssetParams.assetTotal(assetId);
assert(exists);
return total;
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(AssetParamsContract);
const asset = ctx.any.asset({ total: 1000000, decimals: 6 });
const total = contract.getAssetTotal(asset.id);
expect(total).toEqual(1000000);

algopy.op.Box

import type { bytes } from '@algorandfoundation/algorand-typescript';
import { arc4, assert, Bytes, op } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class BoxStorageContract extends arc4.Contract {
@arc4.abimethod()
storeAndRetrieve(key: bytes, value: bytes): bytes {
op.Box.put(key, value);
const [retrievedValue, exists] = op.Box.get(key);
assert(exists);
return retrievedValue;
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(BoxStorageContract);
const key = Bytes('test_key');
const value = Bytes('test_value');
const result = contract.storeAndRetrieve(key, value);
expect(result).toEqual(value);
const storedValue = ctx.ledger.getBox(contract, key);
expect(storedValue).toEqual(value);

algopy.compile_contract

import { arc4, compile, uint64 } from '@algorandfoundation/algorand-typescript';
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing';
class MockContract extends arc4.Contract {}
class ContractFactory extends arc4.Contract {
@arc4.abimethod()
compileAndGetBytes(): uint64 {
const contractResponse = compile(MockContract);
return compiled.localBytes;
}
}
// Create the context manager for snippets below
const ctx = new TestExecutionContext();
const contract = ctx.contract.create(ContractFactory);
const mockApp = ctx.any.application({ localNumBytes: 4 });
ctx.setCompiledApp(MockContract, mockApp.id);
const result = contract.compileAndGetBytes();
expect(result).toBe(4);

Mockable Opcodes

These opcodes are mockable in algorand-typescript-testing, allowing for controlled testing of complex operations. Note that the module being mocked is @algorandfoundation/algorand-typescript-testing/internal which holds the stub implementations of algorand-typescript functions to be executed in Node.js environment.

algopy.op.vrf_verify

import { expect, Mock, test, vi } from 'vitest';
import { bytes, Bytes, op, VrfVerify } from '@algorandfoundation/algorand-typescript';
vi.mock(
import('@algorandfoundation/algorand-typescript-testing/internal'),
async importOriginal => {
const mod = await importOriginal();
return {
...mod,
op: {
...mod.op,
vrfVerify: vi.fn(),
},
};
},
);
test('mock vrfVerify', () => {
const mockedVrfVerify = op.vrfVerify as Mock<typeof op.vrfVerify>;
const mockResult = [Bytes('mock_output'), true] as readonly [bytes, boolean];
mockedVrfVerify.mockReturnValue(mockResult);
const result = op.vrfVerify(
VrfVerify.VrfAlgorand,
Bytes('proof'),
Bytes('message'),
Bytes('public_key'),
);
expect(result).toEqual(mockResult);
});

algopy.op.EllipticCurve

import { expect, Mock, test, vi } from 'vitest';
import { Bytes, op } from '@algorandfoundation/algorand-typescript';
vi.mock(
import('@algorandfoundation/algorand-typescript-testing/internal'),
async importOriginal => {
const mod = await importOriginal();
return {
...mod,
op: {
...mod.op,
EllipticCurve: {
...mod.op.EllipticCurve,
add: vi.fn(),
},
},
};
},
);
test('mock EllipticCurve', () => {
const mockedEllipticCurveAdd = op.EllipticCurve.add as Mock<typeof op.EllipticCurve.add>;
const mockResult = Bytes('mock_output');
mockedEllipticCurveAdd.mockReturnValue(mockResult);
const result = op.EllipticCurve.add(op.Ec.BN254g1, Bytes('A'), Bytes('B'));
expect(result).toEqual(mockResult);
});

These examples demonstrate how to mock key mockable opcodes in algorand-typescript-testing. Use similar techniques (in your preferred testing framework) for other mockable opcodes like mimc, and JsonRef.

Mocking these opcodes allows you to:

  1. Control complex operations’ behavior not covered by implemented and emulated types.
  2. Test edge cases and error conditions.
  3. Isolate contract logic from external dependencies.
// test cleanup
ctx.reset();