v7 migration
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.
<6.1.0 Migration Guide
Section titled “<6.1.0 Migration Guide”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';Migrating
Section titled “Migrating”Step 1 - Accommodate AlgoAmount change
Section titled “Step 1 - Accommodate AlgoAmount change”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.
Step 2 - Replace sdk clients with AlgorandClient
Section titled “Step 2 - Replace sdk clients with AlgorandClient”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 clientconst 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.
Step 3 - Replace function calls
Section titled “Step 3 - Replace function calls”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, orkmdaround - remove those arguments - Consistently using
senderrather thanfrom/senderfor the transaction sender, and this argument is a string rather than taking aSendTransactionFromtype to improve simplicity (so you may need to add.addror similar to an account object) - Transfer receivers are now
receiverrather thantoand always take a string of the address (so you may need to add.addror similar to an account object) clearProgramparameter is renamed toclearStateProgramandextraPagestoextraProgramPagesfor create and update app calls (to align with Algorand protocol names).extraProgramPagesappears as a top-level params property rather than nested in aschemaproperty.- Round numbers, app IDs and asset IDs are now consistently
BigInt’s rather thannumberornumber | bigint - If you previously used
skipSending: truethat 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
atcas a parameter when constructing a transaction that no longer exists; the new equivalent is to usealgorand.newGroup()to obtain aTransactionComposerand 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
SendTransactionFrominstance 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
AccountManageryou will need to register the signer with theAccountManagerfirst. - Note: you can also explicitly pass a
signerin 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
sendParamsproperty are now collapsed into the parent params object
Step 4 - Replace ApplicationClient usage
Section titled “Step 4 - Replace ApplicationClient usage”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
resolveBysemantics, 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
createordeploythen you need anAppFactoryto do that, and then it will in turn give you anAppClientinstance 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.appIdandclient.appAddressonAppClientsince 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). fundAppAccountno longer takes anAlgoAmountdirectly - it always expects the params object (more consistent with other functions)compileis replaced with static methods onAppClientandgetABIMethodParamsis 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
resolveByfunctionality has disappeared in favour of much simpler entrypoints withinalgorand.client - When making an ABI method call, the method arguments property is now
argsrather thanmethodArgs - The foreign reference arrays have been renamed (and are affected by the switch to
BigIntfor app and asset IDs) and appear in the top level params object rather than nested in anargsproperty:boxes->boxReferencesapps->appReferencesassets->assetReferencesaccounts->accountReferences
- The return value for methods that send a transaction will have any ABI return value directly in the
returnproperty rather than theABIReturntype (this behaviour matches what happened in typed clients, but has now been brought down to the underlyingAppClient)
Step 5 - Replace typed app client usage
Section titled “Step 5 - Replace typed app client usage”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
argsproperty within that object extraPagesis no longer nested within aschemaproperty, instead it’s directly on the method call params asextraProgramPagesclient.compose()is nowclient.newGroup()client.compose()....execute()is nowclient.compose()....send()
>=6.1.0 Migration Guide
Section titled “>=6.1.0 Migration Guide”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.
Migrating
Section titled “Migrating”Step 1 - Update imports
Section titled “Step 1 - Update imports”Some imports have changed, which may need to updated. This only applies if you are directly importing the below types:
-
The
AlgokitComposerclass has been renamed toTransactionComposerand 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
AlgorandClientclass 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
ExecuteParamstype has been renamed toSendParamsand moved from/types/composerto/types/transaction./**** Before ****/import { ExecuteParams } from '@algorandfoundation/algokit-utils/types/composer';/**** After ****/import { SendParams } from '@algorandfoundation/algokit-utils/types/transaction';
Step 2 - Accommodate AlgorandClient changes
Section titled “Step 2 - Accommodate AlgorandClient changes”-
algorand.setSuggestedParamsTimeouthas been renamed toalgorand.setSuggestedParamsCacheTimeout/**** Before ****/algorand.setSuggestedParamsTimeout(60_000);/**** After ****/algorand.setSuggestedParamsCacheTimeout(60_000);
Step 3 - Accommodate AlgokitComposer (now TransactionComposer) changes
Section titled “Step 3 - Accommodate AlgokitComposer (now TransactionComposer) changes”-
addMethodCallandaddAppCallmethods have been refined into more specific variants/**** Before ****/const composer = algorand.newGroup().addMethodCall({// ...});/**** After ****/const composer = algorand.newGroup().addAppCallMethodCall({// ...});// orconst composer = algorand.newGroup().addAppCreateMethodCall({// ...});// orconst composer = algorand.newGroup().addAppDeleteMethodCall({// ...});// orconst composer = algorand.newGroup().addAppUpdateMethodCall({// ...});/**** Before ****/const composer = algorand.newGroup().addAppCall({// ...});/**** After ****/const composer = algorand.newGroup().addAppCall({// ...});// orconst composer = algorand.newGroup().addAppCreate({// ...});// orconst composer = algorand.newGroup().addAppDelete({// ...});// orconst composer = algorand.newGroup().addAppUpdate({// ...}); -
clearProgramhas been renamed toclearStateProgram,extraPageshas been renamed toextraProgramPagesin 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,});
Step 4 - Accommodate AlgorandClient transaction related changes
Section titled “Step 4 - Accommodate AlgorandClient transaction related changes”-
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 secondexecuteOptionsobject collapsed into the firstparamsobject/**** 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,});
Step 5 - Accommodate AccountManager changes
Section titled “Step 5 - Accommodate AccountManager changes”-
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 anAlgoAmountandamountis renamed tobalanceandroundtovalidAsOfRound(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.getAssetInformationtoalgorand.asset.getAccountInformation/**** Before ****/const assetInfo = await algorand.account.getAssetInformation('ACCOUNTADDRESS', 1234);/**** After ****/const assetInfo = await algorand.asset.getAccountInformation('ACCOUNTADDRESS', 1234n);
Step 6 - Accommodate ApplicationClient changes
Section titled “Step 6 - Accommodate ApplicationClient changes”- The
algorand.client.getAppClientBy*()methods now return anAppClientrather thanApplicationClient. Refer to Replace ApplicationClient usage for details on how to migrate.
Optional Steps
Section titled “Optional Steps”AlgoKit VSCode AVM Debugger Extension Utils
Section titled “AlgoKit VSCode AVM Debugger Extension Utils”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
persistSourceMapsas it has been deprecated and will throw if called. -
Install the new package:
Terminal window 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.