All skills

Guide for writing TypeScript code with AlgoKit Utils (`@algorandfoundation/algokit-utils`). Use this skill whenever the user is building on Algorand with TypeScript — client setup, account management, payments, asset operations, atomic transaction groups, smart contract deployment and interaction (AppFactory, AppClient, ARC-56/ARC-32 specs), raw app calls, key registration, network management, testing with algorandFixture, error handling, and the low-level crypto primitives under `@algorandfoundation/algokit-utils/crypto` (Ed25519 keygen/signing/verification, SHA-512/256 `hash`, Peikert xHD BIP44 wallets, wrapped-secret patterns). Trigger on imports from `@algorandfoundation/algokit-utils` (incl. `/crypto`, `/testing`, `/transact` subpaths), references to `AlgorandClient`, `AppFactory`, `AppClient`, `AlgoAmount`, `algorandFixture`, `ed25519Generator`, `peikertXHdWalletGenerator`, `hash`, `WrappedEd25519Seed`, or `RawEd25519Signer`. Also on any TypeScript or JavaScript code that builds on Algorand.

Use this Skill: https://skilld.dev/gh/algorand-devrel/algorand-agent-skills/algokit-utils-ts

This session only. Nothing lands on disk.

referencestesting-utilities.md

≈1.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Testing utilities

Set up AlgorandFixture in a test suite

Create a test fixture that provides a pre-configured AlgorandClient, funded test account, and helper utilities against LocalNet (or environment-configured network).

import { algorandFixture } from "@algorandfoundation/algokit-utils/testing";

describe("My contract tests", () => {
  const fixture = algorandFixture();

  beforeEach(fixture.newScope);

  test("can send a payment", async () => {
    const { algorand, testAccount } = fixture.context;

    const result = await algorand.send.payment({
      sender: testAccount,
      receiver: testAccount,
      amount: (1).algo(),
    });

    expect(result.confirmation.confirmedRound).toBeGreaterThan(0n);
  });
});

What just happened: algorandFixture() wired up algod, indexer, and kmd clients (defaulting to LocalNet). Calling fixture.newScope before each test creates a fresh AlgorandClient, a new funded test account (10 ALGO by default), and a clean TransactionLogger. Destructuring fixture.context gives you everything you need inside the test.

Use newScope() for isolated test contexts

Control when a new scope is created — use beforeEach for per-test isolation or beforeAll for shared state across a suite.

import { algorandFixture } from "@algorandfoundation/algokit-utils/testing";

describe("Shared context across tests", () => {
  const fixture = algorandFixture({ testAccountFunding: (20).algo() });

  // One scope for the entire suite — all tests share the same testAccount
  beforeAll(fixture.newScope);

  test("first test", async () => {
    const { algorand, testAccount } = fixture.context;
    // testAccount has ~20 ALGO
    await algorand.send.payment({
      sender: testAccount,
      receiver: testAccount,
      amount: (1).algo(),
    });
  });

  test("second test uses same context", async () => {
    const { testAccount } = fixture.context;
    // Same testAccount as the first test — balance reflects previous sends
    const info = await fixture.algorand.account.getInformation(testAccount);
    expect(info.balance.microAlgo).toBeLessThan(20_000_000n);
  });
});

What just happened: By passing fixture.newScope to beforeAll instead of beforeEach, all tests in the suite share the same AlgorandClient and test account. The testAccountFunding option overrides the default 10 ALGO initial balance.

Generate funded test accounts

Create additional ephemeral accounts on the fly, each automatically funded and registered as a signer on the AlgorandClient.

import { algorandFixture } from "@algorandfoundation/algokit-utils/testing";

describe("Multi-account tests", () => {
  const fixture = algorandFixture();

  beforeEach(fixture.newScope);

  test("transfer between two test accounts", async () => {
    const { algorand, testAccount, generateAccount } = fixture.context;

    const receiver = await generateAccount({ initialFunds: (5).algo() });

    await algorand.send.payment({
      sender: testAccount,
      receiver: receiver,
      amount: (1).algo(),
    });

    const info = await algorand.account.getInformation(receiver);
    expect(info.balance.microAlgo).toBeGreaterThan(5_000_000n);
  });
});

What just happened: generateAccount creates a brand-new random account, funds it from the LocalNet dispenser with the specified amount, and registers its signer on fixture.algorand. You can call it as many times as you need — each account is independent.

Use transactionLogger to track transactions in tests

The fixture's transactionLogger automatically records every transaction ID sent through the proxied algod client.

import { algorandFixture } from "@algorandfoundation/algokit-utils/testing";

describe("Transaction tracking", () => {
  const fixture = algorandFixture();

  beforeEach(fixture.newScope);

  test("logs all sent transaction IDs", async () => {
    const { algorand, testAccount, transactionLogger } = fixture.context;

    await algorand.send.payment({
      sender: testAccount,
      receiver: testAccount,
      amount: (1).algo(),
    });

    await algorand.send.payment({
      sender: testAccount,
      receiver: testAccount,
      amount: (2).algo(),
    });

    // transactionLogger captured both transaction IDs
    expect(transactionLogger.sentTransactionIds).toHaveLength(2);
  });
});

What just happened: Every newScope call creates a fresh TransactionLogger that wraps the algod client via a proxy. All transactions sent through algorand.send.* are intercepted and their IDs stored in transactionLogger.sentTransactionIds. The logger is cleared on each new scope.

Capture and assert logs with AlgoKitLogCaptureFixture

Intercept AlgoKit's internal logger to capture and assert log output during tests.

import {
  algoKitLogCaptureFixture,
  algorandFixture,
} from "@algorandfoundation/algokit-utils/testing";

describe("Log capture", () => {
  const fixture = algorandFixture();
  const logs = algoKitLogCaptureFixture();

  beforeEach(async () => {
    logs.beforeEach();
    await fixture.newScope();
  });
  afterEach(logs.afterEach);

  test("captures AlgoKit log messages", async () => {
    const { algorand, testAccount } = fixture.context;

    await algorand.send.payment({
      sender: testAccount,
      receiver: testAccount,
      amount: (1).algo(),
    });

    // Assert that at least one log was captured
    expect(logs.testLogger.capturedLogs.length).toBeGreaterThan(0);

    // Use getLogSnapshot for deterministic snapshot testing
    const snapshot = logs.testLogger.getLogSnapshot({
      accounts: [testAccount],
    });
    // Account addresses are replaced with ACCOUNT_1, ACCOUNT_2, etc.
    expect(snapshot).not.toContain(testAccount.addr.toString());
  });
});

What just happened: algoKitLogCaptureFixture() swaps AlgoKit's global logger with a TestLogger that records every log line while still forwarding to the original logger. capturedLogs gives you the raw array; getLogSnapshot() substitutes dynamic values (addresses, transaction IDs, app IDs) with deterministic placeholders for snapshot testing. afterEach restores the original logger.

Wait for indexer to catch up in tests

When your test needs to query the indexer after sending transactions, wait for it to index the latest data.

import { algorandFixture } from "@algorandfoundation/algokit-utils/testing";

describe("Indexer queries", () => {
  const fixture = algorandFixture();

  beforeEach(fixture.newScope);

  test("query indexer after sending", async () => {
    const { algorand, testAccount, waitForIndexer, waitForIndexerTransaction } =
      fixture.context;

    const result = await algorand.send.payment({
      sender: testAccount,
      receiver: testAccount,
      amount: (1).algo(),
    });

    // Wait for ALL logged transactions to be indexed
    await waitForIndexer();

    // Or wait for a specific transaction by ID
    const txn = await waitForIndexerTransaction(result.txIds[0]);
    expect(txn.transaction.sender).toBe(testAccount.addr.toString());
  });
});

What just happened: waitForIndexer() polls the indexer every 200 ms (up to 100 retries) until all transactions recorded by the transactionLogger are indexed. waitForIndexerTransaction(txId) does the same for a single transaction ID and returns the full indexer transaction response. Both are essential when testing indexer-dependent logic against LocalNet.

Source: SKILL.md on GitHub

1 alert1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    The skill provides a comprehensive guide for using the AlgoKit Utils TypeScript library. It follows security best practices, such as recommending environment variables for secrets, and uses standard testing mnemonics and placeholder domains in its code examples. No malicious patterns or security risks were detected.

  • Socket1mo

    No alerts

  • Snyk1mo

    Risk: HIGH · 2 issues

Signed by skilld at 4235fb8. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 weeks ago.

Activeupdated 6 months ago

README badge

README badge for algorand-devrel/algorand-agent-skills/algokit-utils-ts