Skip to content

Latest commit

 

History

History
284 lines (204 loc) · 15.6 KB

v3-migration-guide.md

File metadata and controls

284 lines (204 loc) · 15.6 KB

Migration Guide - v3

Version 3 of algokit-utils-ts moved from a stateless function-based interface to a stateful class-based interfaces. This change allows for:

  • Easier and simpler consumption experience guided by IDE autocompletion
  • Less redundant parameter passing (e.g., algod client)
  • Better performance through caching of commonly retrieved values like transaction parameters
  • More consistent and intuitive API design
  • Stronger type safety and better error messages
  • Improved ARC-56 compatibility
  • Feature parity with algokit-utils-ts >= v7 interfaces

The entry point to most functionality in AlgoKit Utils is now available via a single entry-point, the AlgorandClient class.

The v2 interfaces and abstractions will be removed in future major version bumps, however in order to ensure gradual migration, all v2 abstractions are available with respective deprecation warnings. 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.

BREAKING CHANGE: the beta module is now removed, any imports from algokit_utils.beta will now raise an error with a link to a new expected import path. This is due to the fact that the interfaces introduced in beta are now refined and available in the main module.

Migration Steps

In general, your codebase might fall into one of the following migration scenarios:

  • Using algokit-utils-py v2.x only without use of abstractions from beta module
  • Using algokit-utils-py v2.x only and with use of abstractions from beta module
  • Using algokit-utils-py v2.x with algokit-client-generator-py v1.x
  • Using algokit-client-generator-py v1.x only (implies implicit dependency on algokit-utils-py v2.x)

Given that algokit-utils-py v3.x is backwards compatible with algokit-client-generator-py v1.x, the following general guidelines are applicable to all scenarios (note that the order of operations is important to ensure straight-forward migration):

  1. Upgrade to algokit-utils-py v3.x
    • 1.1 (If used) Update imports from algokit_utils.beta to algokit_utils
    • 1.2 Follow hints in deprecation warnings to update your codebase to rely on latest v3 interfaces
  2. Upgrade to algokit-client-generator-py v2.x and regenerate typed clients

The remaining set of guidelines are outlining migrations for specific abstractions that had direct equivalents in algokit-utils-py v2.x.

Prerequisites

It is important to reiterate that if you have previously relied on beta versions of algokit-utils-py v2.x, you will need to update your imports to rely on the new interfaces. Errors thrown during import from beta will provide a description of the new expected import path.

As with v2.x all public abstractions in algokit_utils are available for direct imports from algokit_utils import ..., however underlying modules have been refined to be structured loosely around common AVM domains such as applications, transactions, accounts, assets, etc. See API reference for latest and detailed overview.

Step 1 - Replace SDK Clients with AlgorandClient

First, replace your SDK client initialization with AlgorandClient. Look for get_algod_client calls and replace with an appropriate AlgorandClient initialization:

"""Before"""
import algokit_utils
algod = algokit_utils.get_algod_client()
indexer = algokit_utils.get_indexer_client()

"""After"""
from algokit_utils import AlgorandClient
algorand = AlgorandClient.from_environment()  # or .testnet(), .mainnet(), etc.

During migration, you can still access SDK clients if needed:

algod = algorand.client.algod
indexer = algorand.client.indexer
kmd = algorand.client.kmd

Step 2 - Update Account Management

Account management has moved to algorand.account:

Before:

account = algokit_utils.get_account_from_mnemonic(
    mnemonic=os.getenv("MY_ACCOUNT_MNEMONIC"),
)
dispenser = algokit_utils.get_dispenser_account(algod)

After:

account = algorand.account.from_mnemonic(os.getenv("MY_ACCOUNT_MNEMONIC"))
dispenser = algorand.account.dispenser_from_environment()

Key changes:

  • get_accountaccount.from_environment
  • get_account_from_mnemonicaccount.from_mnemonic
  • get_dispenser_accountaccount.dispenser_from_environment
  • get_localnet_default_accountaccount.localnet_dispenser

Step 3 - Update Transaction Management

Transaction creation and sending is now more structured:

Before:

# Single transaction
result = algokit_utils.transfer_algos(
    from_account=account,
    to_addr="RECEIVER",
    amount=algokit_utils.algos(1),
    algod_client=algod,
)

# Transaction groups
atc = AtomicTransactionComposer()
# ... add transactions ...
result = algokit_utils.execute_atc_with_logic_error(atc, algod)

After:

# Single transaction
result = algorand.send.payment(
    sender=account.address,
    receiver="RECEIVER",
    amount=AlgoAmount.from_algo(1),
)

# Transaction groups
composer = algorand.new_group()
# ... add transactions ...
result = composer.send()

Key changes:

  • transfer_algosalgorand.send.payment
  • transfer_assetalgorand.send.asset_transfer
  • execute_atc_with_logic_errorcomposer.send()
  • Transaction parameters are now more consistently named (e.g., sender instead of from_account)
  • Improved amount handling with dedicated AlgoAmount class (e.g., AlgoAmount.from_algo(1))

Step 4 - Update ApplicationSpecification usage

ApplicationSpecification abstraction is largely identical to v2, however it's been renamed to Arc32Contract to better reflect the fact that it's a contract specification for a specific ARC and addition of Arc56Contract supporting the latest recommended conventions. Hence the main actionable change is to update your import to from algokit_utils import Arc32Contract and rename ApplicationSpecification to Arc32Contract.

You can instantiate an Arc56Contract instance from an Arc32Contract instance using the Arc56Contract.from_arc32 method. For instance:

testing_app_arc32_app_spec = Arc32Contract.from_json(app_spec_json)
arc56_app_spec = Arc56Contract.from_arc32(testing_app_arc32_app_spec)

Despite auto conversion of ARC-32 to ARC-56, we recommend recompiling your contract to a fully compliant ARC-56 specification given that auto conversion would skip populating information that can't be parsed from raw ARC-32.

Step 5 - Replace ApplicationClient usage

The existing ApplicationClient (untyped app client) class is still present until at least v4, 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 app resolution semantics, now have static methods that determine different ways of constructing a client and the constructor itself is very simple (requiring app_id)
  • If you want to call create or deploy then you need an AppFactory to do that, and then it will in turn give you an AppClient 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 of deploy).
  • This means that you can simply access client.app_id and client.app_address on AppClient since these values are known statically and won't change (previously associated calls to app_address, app_id properties potentially required extra API calls as the values weren't always available).
  • Adding fund_app_account which serves as a convenience method to top up the balance of address associated with application.
  • All of the methods that return or execute a transaction (update, call, opt_in, etc.) are now exposed in an interface similar to the one in AlgorandClient, namely (where {call_type} is one of: update / delete / opt_in / close_out / clear_state / call):
    • appClient.create_transaction.{callType} to get a transaction for an ABI method call
    • appClient.send.{call_type} to sign and send a transaction for an ABI method call
    • appClient.params.{call_type} to get a params object for an ABI method call
    • appClient.create_transaction.bare.{call_type} to get a transaction for a bare app call
    • appClient.send.bare.{call_type} to sign and send a transaction for a bare app call
    • appClient.params.bare.{call_type} to get a params object for a bare app call
  • The semantics to resolve the application is now available via simpler entrypoints within algorand.client
  • When making an ABI method call, the method arguments property is are now passed via explicit args field in a parameters dataclass applicable to the method call.
  • The foreign reference arrays have been renamed to align with typed parameters on ts and related core algosdk:
    • boxes -> box_references
    • apps -> app_references
    • assets -> asset_references
    • accounts -> account_references
  • The return value for methods that send a transaction will have any ABI return value directly in the abi_return property rather than the low level algosdk ABIResult type while also automatically decoding values based on provided ARC56 spec.

Step 6 - Replace typed app client usage

Version 2 of the Python 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 v2 algokit-utils-py stateless functions, older typed clients generated using version 1 of the Python typed client generator will work against v3 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 certain changes. Refer to client generator v2 migration guide.

Step 7 - Update AppClient State Management

State management is now more structured and type-safe:

"""Before"""
global_state = app_client.get_global_state()
local_state = app_client.get_local_state(account_address)
box_value = app_client.get_box_value("box_name")

"""After"""
# Global state
global_state = app_client.state.global_state.get_all()
value = app_client.state.global_state.get_value("key_name")
map_value = app_client.state.global_state.get_map_value("map_name", "key")

# Local state
local_state = app_client.state.local_state(account_address).get_all()
value = app_client.state.local_state(account_address).get_value("key_name")
map_value = app_client.state.local_state(account_address).get_map_value("map_name", "key")

# Box storage
box_value = app_client.state.box.get_value("box_name")
boxes = app_client.state.box.get_all()
map_value = app_client.state.box.get_map_value("map_name", "key")

Step 8 - Update Asset Management

Asset management is now more consistent:

"""Before"""
result = algokit_utils.opt_in(algod, account, [asset_id])

"""After"""
result = algorand.send.asset_opt_in(
    params=AssetOptInParams(
        sender=account.address,
        asset_id=asset_id,
    )
)

Breaking Changes

  1. Client Management

    • Removal of standalone client creation functions
    • All clients now accessed through AlgorandClient
  2. Account Management

    • Account creation functions moved to AccountManager accessible via algorand.account property
    • Unified TransactionSignerAccountProtocol with compliant and typed SigningAccount, TransactionSignerAccount, LogicSigAccount, MultiSigAccount classes encapsulating low level algosdk abstractions.
    • Improved typing for account operations, such as obtaining account information from algod, returning a typed information object.
  3. Transaction Management

    • Consistent and intuitive transaction creation and sending interface accessible via algorand.{send|params|create_transaction} properties
    • New transaction composition interface accessible via algorand.new_group
    • Removing necessity to interact with low level and untyped algosdk abstractions for assembling, signing and sending transaction(s).
  4. Application Client

    • Split into AppClient, AppDeployer and AppFactory
    • New intuitive structured interface for creating or sending AppCall|AppMethodCall transactions
    • ARC-56 support along with automatic conversion of specs from ARC-32 to ARC-56
  5. State Management

    • New hierarchical state access available via app_client.state.{global_state|local_state|box} properties
    • Improved typing for state values
    • Support for ARC-56 state schemas
  6. Asset Management

    • Dedicated AssetManager class for asset management accessible via algorand.asset property
    • Improved typing for asset operations, such as obtaining asset information from algod, returning a typed information object.
    • Consistent interface for asset opt-in, transfer, freeze, etc.

Best Practices

  1. Use the new AlgorandClient as the main entry point
  2. Leverage IDE autocompletion to discover available functionality, consult with API reference when unsure
  3. Use the transaction parameter builders for type-safe transaction creation (algorand.params.{})
  4. Use the state accessor patterns for cleaner state management {algorand.state.{}}
  5. Use high level TransactionComposer interface over low level algosdk abstractions (where possible)
  6. Use source maps and debug mode to quickly troubleshoot on-chain errors
  7. Use idempotent deployment patterns with versioning

Troubleshooting

A v2 interface/method/class does not display a deprecation warning correctly or at all

Submit an issue to algokit-utils-py with a description of the problem and the code that is causing it.

Useful scenario of converting v2 to v3 not covered in generic migration guide

If you have a scenario that you think is useful and not covered in the generic migration guide, please submit an issue to algokit-utils-py with a scenario.