Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
13 changes: 8 additions & 5 deletions docs/sidebar.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
{ "label": "Home", "link": "/" },
{
"label": "Concepts",
"autogenerate": { "directory": "concepts" }
},
{
"label": "Capabilities",
"autogenerate": { "directory": "capabilities" }
"items": [
{ "slug": "concepts/algorand-client" },
{ "slug": "concepts/transactions" },
{ "slug": "concepts/account" },
{ "slug": "concepts/asset" },
{ "slug": "concepts/app" },
{ "slug": "concepts/errors-and-debugging" }
]
},
{
"label": "Migration Guides",
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/concepts/account.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ Use `multisig` to register and return a multisig account with one or more signin

Throughout this page, accounts are reached through `algorand.account` — the `AccountManager` instance that an [`AlgorandClient`](/algokit-utils-ts/concepts/algorand-client/) creates and shares. `AccountManager` is the class that gets, creates, and funds accounts, and it keeps track of the signer registered for each address so that transactions sent through [`AlgorandClient`](/algokit-utils-ts/concepts/algorand-client/) or the [`TransactionComposer`](/algokit-utils-ts/concepts/transactions/) are signed by sender automatically, without a signer being specified per transaction.

To get an instance of `AccountManager`, you can use either [`AlgorandClient`](/algokit-utils-ts/concepts/algorand-client/) via `algorand.account` or instantiate it directly (passing in a [`ClientManager`](/algokit-utils-ts/capabilities/client/)):
To get an instance of `AccountManager`, you can use either [`AlgorandClient`](/algokit-utils-ts/concepts/algorand-client/) via `algorand.account` or instantiate it directly (passing in a [`ClientManager`](/algokit-utils-ts/api/types/client-manager/classes/clientmanager/)):

<RemoteCode
src="https://raw.githubusercontent.com/algorandfoundation/algokit-utils-ts/main/examples/concepts/accounts.algo.ts"
Expand Down
154 changes: 154 additions & 0 deletions docs/src/content/docs/concepts/errors-and-debugging.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
title: 'Errors & Debugging'
description: 'AlgoKit Utils decodes failed smart contract calls into logic errors with TEAL source context, lets you transform errors before they raise, and emits traces for the AlgoKit AVM Debugger.'
---

import { CardGrid, LinkCard } from '@astrojs/starlight/components'
import RemoteCode from '/src/components/RemoteCode.astro'

When a smart contract call fails, algod reports a `logic eval error` that identifies a program counter, a position in the compiled program rather than a line of your contract's source. AlgoKit Utils layers three capabilities on top of that raw error. It decodes the failure into a `LogicError` that locates the failing line of [Transaction Execution Approval Language (TEAL)](https://dev.algorand.co/concepts/smart-contracts/avm/) source. It lets you register error transformers that rewrite errors before they raise. And in debug mode it emits simulation traces that the AlgoKit Algorand Virtual Machine (AVM) Debugger extension can step through.

<LinkCard
title="AlgoKit AVM Debugger"
href="https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger"
description="The VS Code extension for stepping through AVM execution traces"
/>

## Read a Logic Error

Any send that fails logic evaluation raises a `LogicError` when the client holds the failing program's source map. Clients created through a factory, or clients that compiled the app's programs themselves, hold one automatically. If the app spec also names an `errorMessage` for that program counter, `AppClient` wraps the `LogicError` as the `cause` of a runtime error:

<RemoteCode
src="https://raw.githubusercontent.com/algorandfoundation/algokit-utils-ts/refs/heads/main/examples/concepts/errors_and_debugging.algo.ts"
snippet="INSPECT_LOGIC_ERROR"
lang="ts"
frame="none"
/>

The parsed details live on `led`: the failing `txId`, the `msg` reported by algod, the program counter as `pc`, and any simulation results as `traces`. `teal_line` is the located TEAL line, and `program` is the decoded TEAL split by line. `logicError.stack` shows the failing line in context with a `<--- Error` marker. Without a source map the error still raises, but `teal_line` is `0`.

<LinkCard
title="Applications"
href="/algokit-utils-ts/concepts/app/"
description="Deploying apps and calling the methods whose failures raise LogicError"
/>

## Reuse Source Maps

Source maps are produced when the programs compile, so a client constructed later doesn't hold them, whether it was built from an app ID or in another process entirely. `exportSourceMaps()` returns an `AppSourceMaps` you can serialize and store. `importSourceMaps()` restores the maps onto a fresh client, re-enabling TEAL line resolution in `LogicError`:

<RemoteCode
src="https://raw.githubusercontent.com/algorandfoundation/algokit-utils-ts/refs/heads/main/examples/concepts/errors_and_debugging.algo.ts"
snippet="EXPORT_IMPORT_SOURCE_MAPS"
lang="ts"
frame="none"
/>

`exportSourceMaps()` throws if the client has not compiled or deployed the app yet.

## Transform Errors

Every send funnels through a `TransactionComposer`. When sending or simulating fails, the error passes through the registered error transformers before it raises. A transformer is an async function that takes the current `Error` and returns an `Error` — either a new one to replace it or the original to leave it unchanged.

`algorand.registerErrorTransformer(fn)` applies a transformer to every group and send the client creates from then on, and `unregisterErrorTransformer(fn)` removes it again:

<RemoteCode
src="https://raw.githubusercontent.com/algorandfoundation/algokit-utils-ts/refs/heads/main/examples/concepts/errors_and_debugging.algo.ts"
snippet="REGISTER_ERROR_TRANSFORMER"
lang="ts"
frame="none"
/>

A single group can carry its own transformers. Register them on the composer instead, where they run in registration order and each receives the previous one's output:

```ts
const composer = algorand.newGroup()
composer.registerErrorTransformer(toDomainError)
```

A transformer must return an `Error`. Returning any other value, or throwing from the transformer, wraps the original error so the send still fails with a usable exception.

The `LogicError` decoding described above is itself an error transformer, registered on the `AlgorandClient` by every `AppClient` at construction.

## Configure the Library

The library reads one process-wide configuration object, the `Config` singleton, imported with `import { Config } from '@algorandfoundation/algokit-utils'`. Its `configure()` method sets:

- `debug` is the master switch for simulation-trace emission.
- `projectRoot` is the directory debug artifacts are written to. It is consumed by the Node debug addon, which auto-detects an AlgoKit project by searching upward for an `.algokit.toml` file, bounded by `maxSearchDepth`.
- `traceAll` writes traces for every send rather than only for failures. It only takes effect while `debug` is on.
- `traceBufferSizeMb` caps stored traces at 256 megabytes by default.
- `populateAppCallResources` sets the library-wide default for the send option of the same name.
- `logger` replaces the logger the library writes through, covered under [Control Logging](#control-logging) below.

<RemoteCode
src="https://raw.githubusercontent.com/algorandfoundation/algokit-utils-ts/refs/heads/main/examples/concepts/errors_and_debugging.algo.ts"
snippet="CONFIGURE_DEBUG"
lang="ts"
frame="none"
/>

<LinkCard
title="Transactions"
href="/algokit-utils-ts/concepts/transactions/"
description="Resource population and the send options that control it"
/>

## Capture Traces for the AVM Debugger

With `debug` on, the library simulates failed sends (and every send when `traceAll` is also on) and emits a `TxnGroupSimulated` event with the simulation response. How those traces become files the AVM Debugger can open depends on the environment.

In Node, install [`algokit-utils-ts-debug`](https://github.com/algorandfoundation/algokit-utils-ts-debug) alongside this package. The addon subscribes to those events and writes AVM-Debugger-compliant traces and source maps under `projectRoot`. Trace files use a `.trace.avm.json` extension. The debug utilities live in a separate package so this one stays isomorphic and does not pull Node filesystem APIs into browser bundles.

In the browser the addon cannot run. Subscribe to the event yourself and persist the payload:

```ts
import { Config, EventType } from '@algorandfoundation/algokit-utils'

Config.configure({ debug: true })
Config.events.on(EventType.TxnGroupSimulated, (eventData) => {
Config.logger.info(JSON.stringify(eventData.simulateResponse.get_obj_for_encoding(), null, 2))
})
```
Comment on lines +105 to +112

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, Claude flagged this because it uses a deprecated SDK api from v2 and will throw if anyone tries to copy it verbatim. We can update it to the latest equivalent, which is much cleaner:

// ...
Config.logger.info(encodeJSON(eventData.simulateResponse))```


Copy the JSON from the console, save it with a `.trace.avm.json` extension, and place it in the `debug_traces` folder of an AlgoKit contract project. If you are not using that layout, the extension presents a file picker as long as the file is inside the VS Code workspace.

Open the trace files with the [AlgoKit AVM Debugger extension](https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger) to step through the TEAL with full source mapping.

## Control Logging

The library logs through `Config.logger`. Suppress a single send with `suppressLog: true` on the send options (or `Config.getLogger(true)` for the null logger), silence the library globally with `nullLogger`, or substitute any compatible logger of your own:

```ts
import { Config } from '@algorandfoundation/algokit-utils'
import { nullLogger } from '@algorandfoundation/algokit-utils/types/logging'

Config.configure({ logger: nullLogger })
```

`Config.withDebug(() => { ... })` runs a callback with `debug` temporarily on, then restores the previous value.

## What's Next

<CardGrid>
<LinkCard
title="Applications"
href="/algokit-utils-ts/concepts/app/"
description="Deploying and calling the contracts whose failures this page decodes"
/>
<LinkCard
title="Transactions"
href="/algokit-utils-ts/concepts/transactions/"
description="Groups, simulation, resource population, and fees"
/>
<LinkCard
title="AlgoKit AVM Debugger"
href="https://marketplace.visualstudio.com/items?itemName=algorandfoundation.algokit-avm-vscode-debugger"
description="The VS Code extension for stepping through AVM execution traces"
/>
<LinkCard
title="LogicError API Reference"
href="/algokit-utils-ts/api/types/logic-error/classes/logicerror/"
description="The LogicError class and parsed logic-error details"
/>
</CardGrid>
51 changes: 23 additions & 28 deletions docs/src/content/docs/concepts/transactions.mdx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
title: "Transactions"
description: "AlgoKit Utils builds every Algorand transaction from a params object and handles signing and submission, with control over fees, leases, multisig and logic-signature authorization, atomic groups, and simulation."
title: 'Transactions'
description: 'AlgoKit Utils builds every Algorand transaction from a params object and handles signing and submission, with control over fees, leases, multisig and logic-signature authorization, atomic groups, and simulation.'
---

import { CardGrid, LinkCard } from "@astrojs/starlight/components";
import RemoteCode from "/src/components/RemoteCode.astro";
import { CardGrid, LinkCard } from '@astrojs/starlight/components'
import RemoteCode from '/src/components/RemoteCode.astro'

Moving Algo, creating an asset, or calling a smart contract all happen the same way on Algorand: as a [transaction](https://dev.algorand.co/concepts/transactions/types/), a signed instruction the network validates and records atomically. AlgoKit Utils builds every one of them through a single model. Learn the shape for a payment and it carries over to every other type.

Expand All @@ -22,24 +22,24 @@ Each send returns a result object with the confirmation, transaction IDs, and an

Every transaction starts as a _params object_: `PaymentParams`, `AssetTransferParams`, `OnlineKeyRegistrationParams`, and so on. Fill in the fields for the operation, then hand the params to one of three interfaces on [`AlgorandClient`](/algokit-utils-ts/concepts/algorand-client/). Which one you pick depends on what you want back:

| Interface | Use when you want to |
| ---------------------------------------- | --------------------------------------------------------------- |
| `algorand.send.<type>(params)` | Build, sign, submit, and wait for confirmation in one call |
| Interface | Use when you want to |
| ------------------------------------------- | ------------------------------------------------------------------ |
| `algorand.send.<type>(params)` | Build, sign, submit, and wait for confirmation in one call |
| `algorand.createTransaction.<type>(params)` | Build the unsigned transaction and handle signing/sending yourself |
| `algorand.newGroup().add<Type>(params)` | Collect several transactions into one atomic group |
| `algorand.newGroup().add<Type>(params)` | Collect several transactions into one atomic group |

The client resolves each transaction's sender to its registered signer, so you rarely pass a signer explicitly. When you do, the `signer` field accepts any account the library provides (including the multisig and logic-signature accounts below) or a raw `algosdk` `TransactionSigner`. Suggested parameters (the fee, current round, and genesis identifiers) are fetched and cached for you. Both are covered under [Algorand Client](/algokit-utils-ts/concepts/algorand-client/) and apply to everything below.

## Send Parameters

The params object describes the transaction itself. It also carries a few fields that control how the transaction is sent, rather than what it contains. Set them alongside the transaction fields on any `algorand.send.<type>` call or a group's `send()`:

| Option | Effect |
| ------------------------------------- | ------------------------------------------------------------------------------- |
| `maxRoundsToWaitForConfirmation` | How many rounds to wait for confirmation before giving up |
| `suppressLog` | Suppress the library's send and confirmation logging |
| `populateAppCallResources` | Auto-populate the resources an app call references (see [Applications](/algokit-utils-ts/concepts/app/)) |
| `coverAppCallInnerTransactionFees` | Have the caller cover inner-transaction fees (see [Applications](/algokit-utils-ts/concepts/app/)) |
| Option | Effect |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `maxRoundsToWaitForConfirmation` | How many rounds to wait for confirmation before giving up |
| `suppressLog` | Suppress the library's send and confirmation logging |
| `populateAppCallResources` | Auto-populate the resources an app call references (see [Applications](/algokit-utils-ts/concepts/app/)) |
| `coverAppCallInnerTransactionFees` | Have the caller cover inner-transaction fees (see [Applications](/algokit-utils-ts/concepts/app/)) |

<RemoteCode
src="https://raw.githubusercontent.com/algorandfoundation/algokit-utils-ts/refs/heads/main/examples/concepts/transactions.algo.ts"
Expand All @@ -54,7 +54,7 @@ The model above named three interfaces for issuing a transaction. Each has a sec

### Send a Payment

A payment moves Algo from a sender to a receiver. Pass a `PaymentParams` with an [`AlgoAmount`](/algokit-utils-ts/capabilities/amount/) to `algorand.send.payment`. The result carries the transaction ID and confirmation:
A payment moves Algo from a sender to a receiver. Pass a `PaymentParams` with an [`AlgoAmount`](/algokit-utils-ts/api/types/amount/classes/algoamount/) to `algorand.send.payment`. The result carries the transaction ID and confirmation:

<RemoteCode
src="https://raw.githubusercontent.com/algorandfoundation/algokit-utils-ts/refs/heads/main/examples/concepts/transactions.algo.ts"
Expand Down Expand Up @@ -134,16 +134,15 @@ The minimal shape is a `ConfirmedTransactionResult`: the transaction and its con

```ts
interface ConfirmedTransactionResult {
transaction: Transaction // the transaction sent
transaction: Transaction // the transaction sent
confirmation: modelsv2.PendingTransactionResponse // its confirmation from algod
}
```

`algorand.send.<type>` returns a `SendSingleTransactionResult`, which combines that confirmation with the group-level arrays the transaction was built from:

```ts
type SendSingleTransactionResult =
SendAtomicTransactionComposerResults & ConfirmedTransactionResult
type SendSingleTransactionResult = SendAtomicTransactionComposerResults & ConfirmedTransactionResult
```

Sending a group returns a `SendAtomicTransactionComposerResults` on its own; simulating returns the same transaction and confirmation arrays without submitting:
Expand All @@ -170,11 +169,11 @@ App-specific sends refine this: an app call adds decoded ABI returns, and creati

Algorand's minimum fee is 1000 microAlgo, and the client applies it by default. A normal transaction needs no fee field at all. Three fields adjust that when you need them:

| Field | Effect |
| ----------- | ---------------------------------------------------------------------------------- |
| `staticFee` | Sets an exact, fixed fee, overriding the calculated one |
| `extraFee` | Adds to the calculated fee (e.g. to cover another transaction's fee in a group) |
| `maxFee` | Caps the fee the client will accept; sending raises if the fee would exceed it |
| Field | Effect |
| ----------- | ------------------------------------------------------------------------------- |
| `staticFee` | Sets an exact, fixed fee, overriding the calculated one |
| `extraFee` | Adds to the calculated fee (e.g. to cover another transaction's fee in a group) |
| `maxFee` | Caps the fee the client will accept; sending raises if the fee would exceed it |

`staticFee` pins the fee to an exact value:

Expand Down Expand Up @@ -301,11 +300,7 @@ The address above is a _contract account_: an escrow whose address is the progra

Rekeying moves the authority to sign for an account to a different address, while the account's own address stays the same. Every params object has a `rekeyTo` field that rekeys the sender as a side effect of the transaction. `algorand.account.rekeyAccount(...)` does it on its own. A rekey can permanently hand control of an account to another key, so the mechanics, the risks, and how to sign for a rekeyed sender afterward live with the account model.

<LinkCard
title="Accounts"
href="/algokit-utils-ts/concepts/account/"
description="Rekeying accounts and signing for a rekeyed sender"
/>
<LinkCard title="Accounts" href="/algokit-utils-ts/concepts/account/" description="Rekeying accounts and signing for a rekeyed sender" />

## Register a Key for Consensus

Expand Down
Loading