Skip to content

AlgoKit Utils TypeScript: v9 to v10 Migration Guide

Version 10 represents a major architectural overhaul of AlgoKit Utils. The library has been decoupled from algosdk and restructured into a modular monorepo with custom-generated API clients. This enables:

  • No algosdk dependency - The library now uses generated clients instead of algosdk
  • Unified AlgorandClient - A single entry point for all Algorand interactions
  • Fluent API - Chainable, discoverable methods via algorand.send.*, algorand.account.*, etc.
  • Type safety - Stronger typing throughout with proper Address class, enums, and interfaces

This guide serves both developers migrating their applications and AI agents performing automated migrations.


v9v10Notes
getAlgoClient()AlgorandClient.defaultLocalNet()LocalNet
getAlgoClient(testnetConfig)AlgorandClient.testNet()TestNet via AlgoNode
getAlgoClient(mainnetConfig)AlgorandClient.mainNet()MainNet via AlgoNode
getAlgoClient(config)AlgorandClient.fromConfig(config)Custom config
Environment-basedAlgorandClient.fromEnvironment()Reads env vars, falls back to LocalNet
Manual clientsAlgorandClient.fromClients({ algod, indexer, kmd })From existing clients
Operationv9v10
PaymenttransferAlgos()algorand.send.payment()
Asset transfertransferAsset()algorand.send.assetTransfer()
Asset opt-inassetOptIn()algorand.send.assetOptIn()
Asset opt-outassetOptOut()algorand.send.assetOptOut()
Ensure fundedensureFunded()algorand.account.ensureFunded()
App deploymentappClient.deploy()appFactory.deploy()
App callappClient.call()appClient.send.call()
Wait for confirmwaitForConfirmation()Automatic in .send()
Raw algodalgod.status().do()algorand.client.algod.status()
v9v10Type Change
txIDtxIdstring
assetIndex / assetIDassetIdnumber → bigint
appIndex / appIDappIdnumber → bigint
methodArgsargs-
boxesboxReferences-
applicationCallappCall-

The library is now a modular monorepo. While most developers will just use the main package, understanding the structure helps with imports:

PackagePurposeReplaces
@algorandfoundation/algokit-algod-clientGenerated Algod clientalgosdk.Algodv2
@algorandfoundation/algokit-indexer-clientGenerated Indexer clientalgosdk.Indexer
@algorandfoundation/algokit-kmd-clientGenerated KMD clientalgosdk.Kmd
@algorandfoundation/algokit-transactTransaction building/signingalgosdk.Transaction, algosdk.AtomicTransactionComposer
@algorandfoundation/algokit-abiABI types, ARC-56 supportalgosdk.ABIMethod, algosdk.ABIType
@algorandfoundation/algokit-commonAddress, utilitiesalgosdk address functions
@algorandfoundation/algokit-cryptoCryptographic utilitiesalgosdk crypto
// Main entry point (most common)
import { AlgorandClient, AlgoAmount } from '@algorandfoundation/algokit-utils'
// Subpath exports for specific needs
import { ... } from '@algorandfoundation/algokit-utils/transact'
import { ... } from '@algorandfoundation/algokit-utils/algod-client'
import { ... } from '@algorandfoundation/algokit-utils/indexer-client'
import { ... } from '@algorandfoundation/algokit-utils/kmd-client'
import { ... } from '@algorandfoundation/algokit-utils/abi'
import { ... } from '@algorandfoundation/algokit-utils/common'
import { ... } from '@algorandfoundation/algokit-utils/testing'

BREAKING: All /types/* imports are deprecated. Types have moved to root-level modules.

// v9 - OLD (deprecated, will show warnings)
import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account'
import { AppSpec } from '@algorandfoundation/algokit-utils/types/app-spec'
import { AlgoAmount } from '@algorandfoundation/algokit-utils/types/amount'
// v10 - NEW
import { AddressWithTransactionSigner } from '@algorandfoundation/algokit-utils/transact'
import { Arc56Contract } from '@algorandfoundation/algokit-utils/abi'
import { AlgoAmount } from '@algorandfoundation/algokit-utils'

AlgorandClient is the unified facade for all Algorand interactions.

v9:

import algosdk from 'algosdk'
import { getAlgoClient, getAlgoIndexerClient } from '@algorandfoundation/algokit-utils'
const algod = getAlgoClient(getAlgoNodeConfig('testnet', 'algod'))
const indexer = getAlgoIndexerClient(getAlgoNodeConfig('testnet', 'indexer'))
// Then pass algod/indexer to every function call
await transferAlgos({ from, to, amount }, algod)

v10:

import { AlgorandClient } from '@algorandfoundation/algokit-utils'
// One-liner for common networks
const algorand = AlgorandClient.defaultLocalNet() // LocalNet
const algorand = AlgorandClient.testNet() // TestNet
const algorand = AlgorandClient.mainNet() // MainNet
// Or from environment variables (falls back to LocalNet)
const algorand = AlgorandClient.fromEnvironment()
// Or custom config
const algorand = AlgorandClient.fromConfig({
algodConfig: { server: 'https://...', token: '...' },
indexerConfig: { server: 'https://...', token: '...' },
})
// All operations through the unified client
await algorand.send.payment({ sender, receiver, amount })

v9:

const info = await algod.accountInformation(addr).do()
const params = await algod.getTransactionParams().do()

v10:

// No more .do() - methods are direct async calls
const info = await algorand.client.algod.accountInformation(addr)
const params = await algorand.client.algod.transactionParams()
// Or use the shorthand
const params = await algorand.getSuggestedParams()

If you need direct client access:

// v9
import algosdk from 'algosdk'
const algod = new algosdk.Algodv2(token, server, port)
// v10
import { AlgodClient } from '@algorandfoundation/algokit-utils/algod-client'
const algod = new AlgodClient({ baseUrl: server, port, token })
// Similarly for Indexer and KMD
import { IndexerClient } from '@algorandfoundation/algokit-utils/indexer-client'
import { KmdClient } from '@algorandfoundation/algokit-utils/kmd-client'

3.1 TransactionSignerAccount → AddressWithTransactionSigner

Section titled “3.1 TransactionSignerAccount → AddressWithTransactionSigner”

The TransactionSignerAccount type is replaced with AddressWithTransactionSigner.

v9:

import { TransactionSignerAccount } from '@algorandfoundation/algokit-utils/types/account'
const account: TransactionSignerAccount = {
addr: 'ADDRESS...',
signer: transactionSigner,
}

v10:

import { AddressWithTransactionSigner } from '@algorandfoundation/algokit-utils/transact'
import { Address } from '@algorandfoundation/algokit-utils/common'
const account: AddressWithTransactionSigner = {
addr: Address.fromString('ADDRESS...'), // Now uses Address object
signer: transactionSigner,
}

v9:

import { SigningAccount } from '@algorandfoundation/algokit-utils'
const signingAccount = new SigningAccount(mnemonic, sender)

v10:

// Use AccountManager instead
const account = algorand.account.fromMnemonic(mnemonic, sender)

3.3 Account Object with Private Key → Signer Pattern

Section titled “3.3 Account Object with Private Key → Signer Pattern”

v10 improves security by not exposing private keys directly.

v9:

import algosdk from 'algosdk'
import { getAccount } from '@algorandfoundation/algokit-utils'
// Account object containing private key
const sender = await getAccount({ name: 'SENDER', algod })
console.log(sender.sk) // Private key exposed

v10:

// Returns AddressWithTransactionSigner - no private key exposed
const sender = await algorand.account.fromEnvironment('SENDER')
// Access address
console.log(sender.addr.toString())
// The signer is used internally - no need to access sk

v9:

import algosdk from 'algosdk'
const account = algosdk.generateAccount()
const address = account.addr

v10:

const account = algorand.account.random()
// account is Address & AddressWithTransactionSigner
const address = account.addr.toString()

v10 introduces a proper Address class and ReadableAddress union type.

import { Address, ReadableAddress, getAddress } from '@algorandfoundation/algokit-utils/common'
// Address is now a class, not a string
const address: Address = Address.fromString('ABC123...')
const addressString: string = address.toString()
// ReadableAddress accepts multiple formats
type ReadableAddress = string | Address | Addressable // Addressable = { addr: Address }
// Convert any ReadableAddress to Address
const addr: Address = getAddress(readableAddress)

Payment:

// v9
import { transferAlgos, algos } from '@algorandfoundation/algokit-utils'
await transferAlgos(
{
from: sender,
to: receiver,
amount: algos(5),
},
algod,
)
// v10
import { AlgoAmount } from '@algorandfoundation/algokit-utils'
await algorand.send.payment({
sender: sender.addr,
receiver: receiver.addr,
amount: AlgoAmount.Algos(5), // or (5).algos()
})

Asset Transfer:

// v9
await transferAsset(
{
from: sender,
to: receiver,
assetID: 123,
amount: 10,
},
algod,
)
// v10
await algorand.send.assetTransfer({
sender: sender.addr,
receiver: receiver.addr,
assetId: 123n, // assetID → assetId, number → bigint
amount: 10n,
})

4.2 AtomicTransactionComposer → TransactionComposer

Section titled “4.2 AtomicTransactionComposer → TransactionComposer”

v9:

import algosdk from 'algosdk'
const atc = new algosdk.AtomicTransactionComposer()
atc.addTransaction({ txn, signer })
const result = await atc.execute(algod, 4)

v10:

const result = await algorand.newGroup().addTransaction(txn).send() // Automatically groups, signs, sends, and waits

v9:

const composer = new algosdk.AtomicTransactionComposer()
composer.addTransaction({
txn: algosdk.makePaymentTxnWithSuggestedParamsFromObject({ ... }),
signer: sender.signer,
})
composer.addMethodCall({
appID: 123,
method: myMethod,
methodArgs: ['arg1'],
sender: sender.addr,
signer: sender.signer,
suggestedParams,
})
await composer.execute(algod, 4)

v10:

const result = await algorand
.newGroup()
.addPayment({
sender: sender.addr,
receiver: receiver.addr,
amount: (1).algos(),
})
.addAppCallMethodCall({
appId: 123n,
method: 'my_method',
args: ['arg1'],
sender: sender.addr,
})
.send() // One call handles everything

Transactions are now built lazily. Fee calculation happens at .build() time.

const composer = algorand.newGroup()
composer.addPayment({ sender, receiver, amount })
// Explicit build step (usually not needed - .send() calls this)
const { transactions, methodCalls, signers } = await composer.build()
// Or just send directly
const result = await composer.send()

v9: Debugging was separate from execution.

v10: Switch any group from .send() to .simulate():

const result = await algorand
.newGroup()
.addPayment({ ... })
.addAppCallMethodCall({ ... })
.simulate({ allowUnnamedResources: true })
console.log(result.simulateResponse)
// Gather signatures without sending
const signatures = await composer.gatherSignatures()
// Simulate with options
const simResult = await composer.simulate({ skipSignatures: true })
// Clone the composer
const cloned = composer.clone()
// Add another composer's transactions
composer.addTransactionComposer(anotherComposer)

Part 5: App Client and Smart Contract Changes

Section titled “Part 5: App Client and Smart Contract Changes”

String-based OnComplete values are replaced with a proper enum.

v9:

const onComplete = 'optin' // or 'noop', 'closeout', etc.

v10:

import { OnApplicationComplete } from '@algorandfoundation/algokit-utils/transact'
const onComplete = OnApplicationComplete.OptIn
// Values: NoOp, OptIn, CloseOut, ClearState, UpdateApplication, DeleteApplication

v10 separates concerns:

  • AppFactory: Handles creating and deploying applications
  • AppClient: Handles interacting with deployed applications

Deployment (v9):

const appClient = algokit.getAppClient(
{
resolveBy: 'creatorAndName',
app: appSpec, // ARC-4 JSON
sender: deployer,
creatorAddress: deployer,
},
algod,
)
const app = await appClient.deploy({
allowUpdate: true,
deployTimeParams: { VALUE: 1 },
})

Deployment (v10):

// 1. Create the factory
const factory = algorand.client.getAppFactory({
appSpec: arc56Spec, // ARC-56 JSON (or ARC-32 - auto-converted)
defaultSender: deployer.addr,
})
// 2. Deploy
const { appClient, result } = await factory.deploy({
updatable: true, // allowUpdate → updatable
deployTimeParams: { VALUE: 1 },
createParams: {
method: 'create',
args: ['arg1'],
},
})

v10 natively supports ARC-56 with automatic ARC-32 conversion.

import { arc32ToArc56 } from '@algorandfoundation/algokit-utils'
// Explicit conversion
const arc56Spec = arc32ToArc56(arc32AppSpec)
// Or just pass ARC-32 directly - it's auto-converted
const appClient = new AppClient({ appSpec: arc32OrArc56Spec, ... })

v9:

await appClient.call({
method: 'hello',
methodArgs: ['world'],
boxes: [{ appIndex: 0, name: 'box1' }],
})

v10:

await appClient.send.call({
method: 'hello',
args: ['world'], // methodArgs → args
boxReferences: ['box1'], // boxes → boxReferences (supports simple strings)
})

v10 introduces unified accessReferences:

v9 (legacy arrays - still supported):

await composer.addAppCall({
appId,
sender,
accountReferences: ['ACCOUNT1'],
appReferences: [123n],
assetReferences: [456n],
boxReferences: [{ appId: 0n, name: 'box1' }],
})

v10 (unified - optional):

await composer.addAppCall({
appId,
sender,
accessReferences: [{ appId: 123n }, { assetId: 456n }, { appId: 0n, boxName: 'box1' }],
})

v9:

const returnValue = algosdk.AtomicTransactionComposer.parseMethodResponse(method, response)

v10:

import { AppManager } from '@algorandfoundation/algokit-utils'
const abiReturn = AppManager.getABIReturn(confirmation, method)

v9:

import { indexer } from '@algorandfoundation/algokit-utils'
await indexer.lookupAssetHoldings(indexerClient, assetId)

v10:

import { lookupAssetHoldings } from '@algorandfoundation/algokit-utils/indexer-client'
await lookupAssetHoldings(indexerClient, assetId)

6.2 searchTransactions - Breaking API Change

Section titled “6.2 searchTransactions - Breaking API Change”

The searchCriteria parameter changed from a function to an object.

v9:

import { indexer } from '@algorandfoundation/algokit-utils'
const results = await indexer.searchTransactions(
indexerClient,
(s) => s.address('ADDRESS').txType('pay'), // Function-based
)

v10:

import { searchTransactions } from '@algorandfoundation/algokit-utils/indexer-client'
const results = await searchTransactions(indexerClient, {
address: 'ADDRESS',
txType: 'pay',
}) // Object-based

v9:

import { algorandFixture } from '@algorandfoundation/algokit-utils/testing'
const localnet = algorandFixture()
beforeEach(localnet.beforeEach) // Deprecated
test('my test', async () => {
const { algod, indexer, testAccount } = localnet.context
// logic using algod directly...
})

v10:

import { algorandFixture } from '@algorandfoundation/algokit-utils/testing'
const localnet = algorandFixture()
beforeEach(localnet.newScope) // beforeEach → newScope (semantic rename)
test('my test', async () => {
const { algorand, testAccount } = localnet.context
// Use the fluent client in tests
await algorand.send.payment({
sender: testAccount.addr,
receiver: testAccount.addr,
amount: (1).algos(),
})
})

Note: If you still need raw algod in tests, access via algorand.client.algod.

v9:

const account = await getTestAccount(params, algodClient, kmdClient)

v10:

// Preferred - use AlgorandClient
const account = await getTestAccount(params, algorandClient)
// Old signature deprecated but still works
const account = await getTestAccount(params, algodClient, kmdClient)

Test accounts now return AddressWithSigners instead of AddressWithTransactionSigner:

// v9 return type
Promise<Address & AddressWithTransactionSigner>
// v10 return type
Promise<Address & AddressWithSigners>

populateAppCallResources now defaults to true (was false).

Impact: App calls will automatically populate resources. To preserve v9 behavior:

await algorand.send.appCall({
...,
populateAppCallResources: false, // Explicit opt-out
})
// v9
import { ... } from '@algorandfoundation/algokit-utils/types/config'
// v10
import { UpdatableConfig } from '@algorandfoundation/algokit-utils'

FunctionReplacement
getApplicationClient()algorand.client.getAppClient()
getAppByIndex()Use AppClient directly
appClient.getBoxValuesAsABIType()Removed
getABISignature()Removed
FunctionReplacement
populateAppCallResources()composer.build() (auto-populates)
prepareGroupForSending()composer.setMaxFees() + composer.build()
sendTransactionComposer()composer.send()
performTransactionComposerSimulate()composer.simulate()
transferAlgos()algorand.send.payment()
transferAsset()algorand.send.assetTransfer()
ensureFunded()algorand.account.ensureFunded()
TypeReplacement
TransactionSignerAccountAddressWithTransactionSigner
SendTransactionFromSendingAddress
SigningAccountUse accountManager.fromMnemonic()

BREAKING: The trace field structure changed.

// v9 - trace was a map
err.traces[0].trace // Map structure
// v10 - trace is a typed value
err.traces[0].trace // Typed object structure

Terminal window
npm install @algorandfoundation/algokit-utils@latest
npm uninstall algosdk # No longer needed as direct dependency
  • Replace getAlgoClient() with AlgorandClient.*
  • Remove manual algod/indexer/kmd client creation
  • Use AlgorandClient.fromEnvironment() for env-based config
  • Replace algosdk imports with algokit-utils subpath imports
  • Move /types/* imports to root or subpath imports
  • Move indexer imports to /indexer-client
  • Import OnApplicationComplete from /transact
  • Import Address, ReadableAddress from /common
  • Import ABIMethod, ABIType from /abi
  • TransactionSignerAccountAddressWithTransactionSigner
  • Remove SigningAccount, use algorand.account.fromMnemonic()
  • Use address.toString() for string representation
  • Update signer patterns (no more account.sk)
  • transferAlgos()algorand.send.payment()
  • transferAsset()algorand.send.assetTransfer()
  • Replace AtomicTransactionComposer with algorand.newGroup()
  • Remove .do() from all client calls
  • assetIDassetId (bigint)
  • appIDappId (bigint)
  • txIDtxId
  • methodArgsargs
  • boxesboxReferences
  • String OnComplete → OnApplicationComplete enum
  • appClient.call()appClient.send.call()
  • Use AppFactory for deployments
  • atc.parseMethodResponse()AppManager.getABIReturn()
  • Move imports to /indexer-client
  • searchTransactions function → object criteria
  • fixture.beforeEachfixture.newScope
  • getTestAccount(params, algod)getTestAccount(params, algorand)
  • Update return type expectations (AddressWithSigners)
  • Run all tests
  • Check for deprecation warnings
  • Test transaction signing/sending
  • Test app client interactions
  • Test indexer queries