App client

Application client that works with ARC-0032 application spec defined smart contracts (e.g. via Beaker).

App client is a high productivity application client that works with ARC-0032 application spec defined smart contracts, which you can use to create, update, delete, deploy and call a smart contract and access state data for it.

To see some usage examples check out the automated tests.

Design

The design for the app client is based on a wrapper for parsing an ARC-0032 application spec and wrapping the App deployment functionality and corresponding design.

Creating an application client

There are two key ways of instantiating an ApplicationClient:

  1. By app ID - When needing to call an existing app by app ID or unconditionally create a new app. The signature ApplicationClient(algod_client, app_spec, app_id=..., ...) requires:

    • algod_client: An AlgodClient

    • app_spec: An ApplicationSpecification

    • app_id: The app_id of an existing application, or 0 if creating a new app

  2. By creator and app name - When needing to deploy or find an app associated with a specific creator account and app name. The signature ApplicationClient(algod_client, app_spec, creator=..., indexer=..., app_lookup) requires:

    • algod_client: An AlgodClient

    • app_spec: An ApplicationSpecification

    • creator: The address or Account of the creator of the app for which to search for the deployed app under

    • indexer:

    • app_lookup: Optional if an indexer is provided,

    • app_name: An overridden name to identify the contract with, otherwise contract.name is used from the app spec

Both approaches also allow specifying the following parameters that will be used as defaults for all application calls:

  • signer: TransactionSigner to sign transactions with.

  • sender: Address to use for transaction signing, will be derived from the signer if not provided.

  • suggested_params: Default SuggestedParams to use, will use current network suggested params by default

Both approaches also allow specifying a mapping of template values via the template_values parameter, this will be used before compiling the application to replace any TMPL_ variables that may be in the TEAL. The TMPL_UPDATABLE and TMPL_DELETABLE variables used in some AlgoKit templates are handled by the deploy method, but should be included if using create or update directly.

Calling methods on the app

There are various methods available on ApplicationClient that can be used to call an app:

  • call: Used to call methods with an on complete action of no_op

  • create: Used to create an instance of the app, by using an app_id of 0, includes the approval and clear programs in the call

  • update: Used to update an existing app, includes the approval and clear programs in the call, and is called with an on complete action of update_application

  • delete: Used to remove an existing app, is called with an on complete action of delete_application

  • opt_in: Used to opt in to an existing app, is called with an on complete action of opt_in

  • close_out: Used to close out of an existing app, is called with an on complete action of opt_in

  • clear_state: Used to unconditionally close out from an app, calls the clear program of an app

Specifying which method

All methods for calling an app that support ABI methods (everything except clear_state) take a parameter call_abi_method which can be used to specify which method to call. The method selected can be specified explicitly, or allow the client to infer the method where possible, supported values are:

  • None: The default value, when None is passed the client will attempt to find any ABI method or bare method that is compatible with the provided arguments

  • False: Indicates that an ABI method should not be used, and instead a bare method call is made

  • True: Indicates that an ABI method should be used, and the client will attempt to find an ABI method that is compatible with the provided arguments

  • str: If a string is provided, it will be interpreted as either an ABI signature specifying a method, or as an ABI method name

  • algosdk.abi.Method: The specified ABI method will be called

  • ABIReturnSubroutine: Any type that has a method_spec function that returns an algosd.abi.Method

ABI arguments

ABI arguments are passed as python keyword arguments e.g. to pass the ABI parameter name for the ABI method hello the following syntax is used client.call("hello", name="world")

Transaction Parameters

All methods for calling an app take an optional transaction_parameters argument, with the following supported parameters:

  • signer: The TransactionSigner to use on the call. This overrides any signer specified on the client

  • sender: The address of the sender to use on the call, must be able to be signed for by the signer. This overrides any sender specified on the client

  • suggested_params: SuggestedParams to use on the call. This overrides any suggested_params specified on the client

  • note: Note to include in the transaction

  • lease: Lease parameter for the transaction

  • boxes: A sequence of boxes to use in the transaction, this is a list of (app_index, box_name) tuples [(0, "box_name"), (0, ...)]

  • accounts: Account references to include in the transaction

  • foreign_apps: Foreign apps to include in the transaction

  • foreign_assets: Foreign assets to include in the transaction

  • on_complete: The on complete action to use for the transaction, only available when using call or create

  • extra_pages: Additional pages to allocate when calling create, by default a sufficient amount will be calculated based on the current approval and clear. This can be overridden, if more is required for a future update

Parameters can be passed as one of the dataclasses CommonCallParameters, OnCompleteCallParameters, CreateCallParameters (exact type depends on method used)

client.call("hello", transaction_parameters=algokit_utils.OnCompleteCallParameters(signer=...))

Alternatively, parameters can be passed as a dictionary e.g.

client.call("hello", transaction_parameters={"signer":...})

Composing calls

If multiple calls need to be made in a single transaction, the compose_ method variants can be used. All these methods take an AtomicTransactionComposer as their first argument. Once all the calls have been added to the ATC, it can then be executed. For example:

from algokit_utils import ApplicationClient
from algosdk.atomic_transaction_composer import AtomicTransactionComposer

client = ApplicationClient(...)
atc = AtomicTransactionComposer()
client.compose_call(atc, "hello", name="world")
... # additional compose calls

response = client.execute_atc(atc)

Reading state

There are various methods defined that let you read state from the smart contract app:

  • get_global_state - Gets the current global state of the app

  • get_local_state - Gets the current local state for the given account address

Handling logic errors and diagnosing errors

Often when calling a smart contract during development you will get logic errors that cause an exception to throw. This may be because of a failing assertion, a lack of fees, exhaustion of opcode budget, or any number of other reasons.

When this occurs, you will generally get an error that looks something like: TransactionPool.Remember: transaction {TRANSACTION_ID}: logic eval error: {ERROR_MESSAGE}. Details: pc={PROGRAM_COUNTER_VALUE}, opcodes={LIST_OF_OP_CODES}.

The information in that error message can be parsed and when combined with the source map from compilation you can expose debugging information that makes it much easier to understand what’s happening.

When an error is thrown then the resulting error that is re-thrown will be a LogicError, which has the following fields:

  • logic_error: Original exception

  • program: Program source (if available)

  • source_map: Source map used (if available)

  • transaction_id: Transaction ID of failing transaction

  • message: The error message

  • line_no: The line number in the TEAL program that

  • traces: A list of Trace objects providing additional insights on simulation when debug mode is active.

The function trace() will provide a formatted output of the surrounding TEAL where the error occurred.

Note

The extended information will only show if the Application Client has a source map. This will occur if:

1.) The ApplicationClient instance has already called, create, updateordeployOR 2.)template_valuesare provided when creating the ApplicationClient, so a SourceMap can be obtained automatically OR 3.)approval_source_maponApplicationClienthas been set from a previously compiled approval program OR 4.) A source map has been exported/imported usingexport_source_map/import_source_map`”””

Debug Mode and traces Field

When debug mode is active, the LogicError will contain a field named traces. This field will include raw simulate execution traces, providing a detailed account of the transaction simulation. These traces are crucial for diagnosing complex issues and are automatically included in all application client calls when debug mode is active.

Note

Remember to enable debug mode (config.debug = True) to include raw simulate execution traces in the LogicError.