Version 7 of AlgoKit Utils moved from a stateless function-based interface to a stateful class-based interface. Doing this allowed for a much easier and simpler consumption experience guided by intellisense, involves less passing around of redundant values (e.g. algod
client) and is more performant since commonly retrieved values like transaction parameters are able to be cached.
The entry point to the vast majority of functionality in AlgoKit Utils is now available via a single entry-point, the AlgorandClient
class.
The old version will still work until at least v9 (we have been careful to keep those functions working with backwards compatibility), but it exposes an older, function-based interface to the functionality that is deprecated. The new way to use AlgoKit Utils is via the AlgorandClient
class, which is easier, simpler and more convenient to use and has powerful new features.
An early version of the AlgorandClient
was released in v6.1.0. The intention was to evolve the stateful class-based interface without any breaking changes, however unfortunately that wasn't possible.
As a result we have 2 supported migration paths: <6.1.0 and >=6.1.0.
We have been diligently adding JSDoc deprecations to the code. We recommend that after reading this guide you leverage the deprecation messages inside your IDE to guide you through the migration process.
A simple example of the before and after follows:
/**** Before ****/
import * as algokit from '@algorandfoundation/algokit-utils'
const algod = algokit.getAlgoClient()
const account = await algokit.mnemonicAccountFromEnvironment(
{
name: 'MY_ACCOUNT',
fundWith: algokit.algos(2),
},
algod,
)
const payment = await algokit.transferAlgos({
from: account,
to: 'RECEIVER',
amount: algokit.algos(1),
})
/**** After ****/
import { AlgorandClient } from '@algorandfoundation/algokit-utils'
const algorand = await AlgorandClient.fromEnvironment()
const account = await algorand.account.fromEnvironment('MY_ACCOUNT', (2).algo())
const payment = await algorand.send.payment({
sender: account.addr,
receiver: 'RECEIVER',
amount: (1).algo(),
})
If you were following the recommended guidance for AlgoKit Utils then you can easily tell if you are using the old version by looking for this import line (which can act as a good todo checklist if you are migrating):
import * as algokit from '@algorandfoundation/algokit-utils'
There is a class in AlgoKit Utils called AlgoAmount
that wraps the representation of microAlgo / Algo amounts. The microAlgo
property on that class now returns a bigint
rather than a number
, which is a breaking change. This is to align with the new consistent way of representing certain types of values (in this case Algo balances and microAlgo amounts) as bigints.
The next step is to get an AlgorandClient
instance at the same place(s) you had an algod instance. To do this you can look for anywhere you called the getAlgoClient
method and replace them with an equivalent mechanism for getting an AlgorandClient
instance.
You can retrieve an algod / indexer / kmd object to avoid the need to immediately have to rewrite all of the old calls by accessing them from the AlgorandClient
instance, e.g.:
const algorand = AlgorandClient.mainnet() // ... or whichever other method you want to get a client
const algod = algorand.client.algod
// And if you need these...
const indexer = algorand.client.indexer
const kmd = algorand.client.kmd
Once you have fully migrated you will likely find you won't need these sdk client instances and can delete those variables.
Now you can replace the function calls one-by-one. Almost every call should have a @deprecation
notice that will show up in intellisense for your IDE (e.g. VS Code). The method call will show up with strikethrough and if you hover over it then the deprecation notice will show the new functionality.
For instance, the algokit.transferAlgos
call shown in the above example has the following deprecation notice:
@deprecated Use
algorand.send.payment()
/algorand.createTransaction.payment()
instead
Note: Anywhere the term algorand.*
is used in the deprecation messages, it's referring to the instance of the AlgorandClient
you created in Step 2.
These deprecation notices should largely let you follow the bouncing ball and make quick work of the migration. The old vs new calls are fairly equivalent with some naming changes to improve consistency within AlgoKit Utils and more broadly to align to the core Algorand protocol (e.g. using payment
rather than transferAlgos
since it's a payment transaction on chain). In saying that, there are some key differences that you will need to tweak:
- No longer any need to pass
algod
,indexer
, orkmd
around - remove those arguments - Consistently using
sender
rather thanfrom
/sender
for the transaction sender, and this argument is a string rather than taking aSendTransactionFrom
type to improve simplicity (so you may need to add.addr
or similar to an account object) - Transfer receivers are now
receiver
rather thanto
and always take a string of the address (so you may need to add.addr
or similar to an account object) clearProgram
parameter is renamed toclearStateProgram
andextraPages
toextraProgramPages
for create and update app calls (to align with Algorand protocol names).extraProgramPages
appears as a top-level params property rather than nested in aschema
property.- Round numbers, app IDs and asset IDs are now consistently
BigInt
's rather thannumber
ornumber | bigint
- If you previously used
skipSending: true
that no longer exists; the new equivalent of that is to usealgorand.createTransaction...
, but otherwise you should usealgorand.send...
to immediately sign and send. - If you previously used
atc
as a parameter when constructing a transaction that no longer exists; the new equivalent is to usealgorand.newGroup()
to obtain aTransactionComposer
and chain method calls to build up a group of transactions and then callexecute()
to execute the group. - Functions that took multiple params objects largely only take a single, combined object now (intellisense is your friend, ctrl+space or your IDE's equivalent auto-complete keyboard shortcut will help you see all of the options!).
Other things to note that you may come across:
- We now restrict the number of valid rounds (10 rounds, except when pointing to LocalNet, which is still 1000 to avoid problems given the round advances for every transaction) to a much smaller window than the default (1000 rounds), but this is configurable by default and per transaction if that's a problem. If you come across this problem it will present as a dead transaction error.
- Transaction parameters are cached for a period of time to prevent repeated calls to the algod API to get default transaction parameters, but this sometimes means that you can create duplicate transaction IDs when previously that wouldn't happen, you will get an obvious error if that happens though and can adjust it by ensuring one of the parameters in your transaction change slightly (e.g. note, lease, validity window, etc.), you can also exert control over default transaction parameter caching
- Rather than always passing a signer into transaction calls (which is what the
SendTransactionFrom
instance previously combined with the address), we have decoupled signing and sender address via theAccountManager
(algorand.account
), which keeps track of the signer associated with a sender address so the signer can be resolved just in time.- Most of the time you don't need to worry about it since it will magically happen for you, but if you have situations where you are creating a signer outside of the
AccountManager
you will need to register the signer with theAccountManager
first. - Note: you can also explicitly pass a
signer
in as well if you want an escape hatch.
- Most of the time you don't need to worry about it since it will magically happen for you, but if you have situations where you are creating a signer outside of the
- Things that were previously nested in a
sendParams
property are now collapsed into the parent params object
The existing ApplicationClient
(untyped app client) class is still present until at least v9, but it's worthwhile migrating to the new AppClient
and AppFactory
classes. These new clients are ARC-56 compatible, but also support ARC-32 app specs and will continue to support this indefinitely until such time the community deems they are deprecated.
All of the functionality in ApplicationClient
is available within the new classes, but their interface is slightly different to make it easier to use and more consistent with the new AlgorandClient
functionality. The key existing methods that have changed all have @deprecation
notices to help guide you on this, but broadly the changes are:
- The constructor no longer has the confusing
resolveBy
semantics, instead there are static methods that determine different ways of constructing a client and the constructor itself is very simple (requiringappId
) - If you want to call
create
ordeploy
then you need anAppFactory
to do that, and then it will in turn give you anAppClient
instance that is connected to the app you just created / deployed. This significantly simplifies the app client because now the app client has a clear operating purpose: allow for calls and state management for an instance of an app, whereas the app factory handles all of the calls when you don't have an instance yet (or may or may not have an instance in the case ofdeploy
). - This means that you can simply access
client.appId
andclient.appAddress
onAppClient
since these values are known statically and won't change (previously you had to awkwardly callawait client.getAppReference()
since these values weren't always available and potentially required an API call to resolve). fundAppAccount
no longer takes anAlgoAmount
directly - it always expects the params object (more consistent with other functions)compile
is replaced with static methods onAppClient
andgetABIMethodParams
is deprecated in favour ofgetABIMethod
, which now returns the params and theABIMethod
- All of the methods that return or execute a transaction (
update
,call
,optIn
, etc.) are now exposed in an interface similar to the one inAlgorandClient
, namely (where{callType}
is one of:update
/delete
/optIn
/closeOut
/clearState
/call
):appClient.createTransaction.{callType}
to get a transaction for an ABI method callappClient.send.{callType}
to sign and send a transaction for an ABI method callappClient.params.{callType}
to get a params object for an ABI method callappClient.createTransaction.bare.{callType}
to get a transaction for a bare app callappClient.send.bare.{callType}
to sign and send a transaction for a bare app callappClient.params.bare.{callType}
to get a params object for a bare app call
- The
resolveBy
functionality has disappeared in favour of much simpler entrypoints withinalgorand.client
- When making an ABI method call, the method arguments property is now
args
rather thanmethodArgs
- The foreign reference arrays have been renamed (and are affected by the switch to
BigInt
for app and asset IDs) and appear in the top level params object rather than nested in anargs
property:boxes
->boxReferences
apps
->appReferences
assets
->assetReferences
accounts
->accountReferences
- The return value for methods that send a transaction will have any ABI return value directly in the
return
property rather than theABIReturn
type (this behaviour matches what happened in typed clients, but has now been brought down to the underlyingAppClient
)
Version 4 of the TypeScript typed app client generator introduces breaking changes to the generated client that support the new AppFactory
and AppClient
functionality along with adding ARC-56 support. The generated client has better typing support for things like state commensurate with the new capabilities within ARC-56.
It's worth noting that because we have maintained backwards compatibility with the pre v6.1.0 stateless functions, older typed clients generated using version 3 of the TypeScript typed client generator will work against v7 and v8 of utils, however you won't have access to the new features or ARC-56 support.
If you want to convert from an older typed client to a new one you will need to make the following changes:
- The constructor parameters for a client are different per the above notes about
AppClient
, the recommended way of constructing a client / factory is viaalgorand.client.getTyped*()
for a terse creation experience - The app client no longer creates or deploys contracts, you need to use the factory for that, which will in turn give you a typed client instance
- Method calls are no longer directly on the typed client, instead they are nested via
appClient.send.
andappClient.createTransaction.
- Method calls take a single params object (rather than (args, params)) and the args are nested in an
args
property within that object extraPages
is no longer nested within aschema
property, instead it's directly on the method call params asextraProgramPages
client.compose()
is nowclient.newGroup()
client.compose()....execute()
is nowclient.compose()....send()
Assuming you have started using the early version of the AlgorandClient
, then you need to be aware of some breaking changes that we have made to accommodate the feature set of v7. Any migration information related to the stateless function based interface is available in the <6.1.0 Migration Guide.
Some imports have changed, which may need to updated. This only applies if you are directly importing the below types:
-
The
AlgokitComposer
class has been renamed toTransactionComposer
and has been made a named (previously default) export./**** Before ****/ import AlgokitComposer from '@algorandfoundation/algokit-utils/types/composer' const composer = new AlgokitComposer({ //... }) /**** After ****/ import { TransactionComposer } from '@algorandfoundation/algokit-utils/types/composer' const composer = new TransactionComposer({ //... })
-
The
AlgorandClient
class is no longer available as a default export./**** Before ****/ import AlgorandClient from '../../types/algorand-client' const algorand = AlgorandClient.fromClients({ //... }) /**** After ****/ import { AlgorandClient } from '../../types/algorand-client' const algorand = AlgorandClient.fromClients({ //... })
-
The
ExecuteParams
type has been renamed toSendParams
and moved from/types/composer
to/types/transaction
./**** Before ****/ import { ExecuteParams } from '@algorandfoundation/algokit-utils/types/composer' /**** After ****/ import { SendParams } from '@algorandfoundation/algokit-utils/types/transaction'
-
algorand.setSuggestedParamsTimeout
has been renamed toalgorand.setSuggestedParamsCacheTimeout
/**** Before ****/ algorand.setSuggestedParamsTimeout(60_000) /**** After ****/ algorand.setSuggestedParamsCacheTimeout(60_000)
-
addMethodCall
andaddAppCall
methods have been refined into more specific variants/**** Before ****/ const composer = algorand.newGroup().addMethodCall({ // ... }) /**** After ****/ const composer = algorand.newGroup().addAppCallMethodCall({ // ... }) // or const composer = algorand.newGroup().addAppCreateMethodCall({ // ... }) // or const composer = algorand.newGroup().addAppDeleteMethodCall({ // ... }) // or const composer = algorand.newGroup().addAppUpdateMethodCall({ // ... })
/**** Before ****/ const composer = algorand.newGroup().addAppCall({ // ... }) /**** After ****/ const composer = algorand.newGroup().addAppCall({ // ... }) // or const composer = algorand.newGroup().addAppCreate({ // ... }) // or const composer = algorand.newGroup().addAppDelete({ // ... }) // or const composer = algorand.newGroup().addAppUpdate({ // ... })
-
clearProgram
has been renamed toclearStateProgram
,extraPages
has been renamed toextraProgramPages
in the app call params to match the algod api/**** Before ****/ const composer = algorand.newGroup().addAppCall({ sender: 'SENDER', approvalProgram, clearProgram, extraPages, }) /**** After ****/ const composer = algorand.newGroup().addAppCreate({ sender: 'SENDER', approvalProgram, clearStateProgram, extraProgramPages, })
-
algorand.transactions.*
has been renamed toalgorand.createTransaction.*
/**** Before ****/ const payment = await algorand.transactions.payment({ sender: 'SENDER', receiver: 'RECEIVER', amount: (1000).microAlgo() }) /**** After ****/ const payment = await algorand.createTransaction.payment({ sender: 'SENDER', receiver: 'RECEIVER', amount: (1000).microAlgo() })
-
algorand.send.*(params, executeOptions)
has had the secondexecuteOptions
object collapsed into the firstparams
object/**** Before ****/ await algorand.send.payment( { sender: alice.addr, assetId: assetId, signer: alice, }, { maxRoundsToWaitForConfirmation: 100, }, ) /**** After ****/ await algorand.send.payment({ sender: alice.addr, assetId: assetId, signer: alice, maxRoundsToWaitForConfirmation: 100, })
-
The order of the
algorand.account.rekeyed()
parameters has been switched to(sender, signer)
/**** Before ****/ algorand.account.rekeyed(signer, 'SENDER') /**** After ****/ algorand.account.rekeyed('SENDER', signer)
-
All microAlgo return values from
algorand.account.getInformation()
now return anAlgoAmount
andamount
is renamed tobalance
andround
tovalidAsOfRound
(which is now a bigint for broader consistency)/**** Before ****/ const { amount, round } = algorand.account.getInformation('ACCOUNTADDRESS') const algoBalance = algosdk.microalgosToAlgos(amount) /**** After ****/ const { balance, validAsOfRound } = algorand.account.getInformation('ACCOUNTADDRESS') const algoBalance = balance.algo
-
Renamed
algorand.account.getAssetInformation
toalgorand.asset.getAccountInformation
/**** Before ****/ const assetInfo = await algorand.account.getAssetInformation('ACCOUNTADDRESS', 1234) /**** After ****/ const assetInfo = await algorand.asset.getAccountInformation('ACCOUNTADDRESS', 1234n)
- The
algorand.client.getAppClientBy*()
methods now return anAppClient
rather thanApplicationClient
. Refer to Replace ApplicationClient usage for details on how to migrate.
To enable TEAL debugging, AlgoKit Utils would store AVM simulate traces and TEAL sourcemaps when Config.configure({ debug: true })
was used.
Due to issues with browser bundlers, we made a decision to move this functionality to a new optional package algokit-utils-ts-debug
.
This change makes algokit-utils
isomorphic again, however does require you to install an additional package if you want to continue to store these artefacts in your Node based projects.
Additionally we have updated the debug experience to support debugging Algorand Python using the source map generated when compiling using puya
. Coupled with a simulate trace, you can now launch a debug session without a program sources description file (sources.avm.json).
If you'd like to continue to save the debug artefacts in your Node projects, you can migrate using the below:
-
Remove any explicit calls to
persistSourceMaps
as it has been deprecated and will throw if called. -
Install the new package:
npm i @algorandfoundation/algokit-utils-debug
-
Activate the new package:
import { Config } from '@algorandfoundation/algokit-utils' import { registerDebugEventHandlers } from '@algorandfoundation/algokit-utils-debug' Config.configure({ debug: true }) registerDebugEventHandlers()
This approach maintains debug functionality while ensuring compatibility with frontend bundlers.
For more details on debugging puya based contracts, refer here.