Application Client

The ApplicationClient provides a convenient way to interact with our Application.

    # Create Application client
    app_client1 = client.ApplicationClient(
        client=localnet.get_algod_client(), app=nicknames.app, signer=acct1.signer
    )

By passing an AlgodClient, an instance of our Application and a TransactionSigner, we can easily make calls to our application.

Note

The AlgodClient passed to the ApplicationClient is always one pointing to the localnet in examples. This is not a requirement, it can be connected to any Algod node and the most common API providers are available in beaker.client.api_providers

If the application does not yet exist, the app_id argument can be omitted but the first interaction with the Application should be to create it.

If create is called, the app_id will be set for the lifetime of the ApplicationClient instance.

If the application does exist, the app_id can be provided directly

    # Create a new client that just sets the app id we wish to interact with
    app_client3 = ApplicationClient(
        client=localnet.get_algod_client(),
        app=nicknames.app,
        signer=acct1.signer,
        app_id=app_client1.app_id,
    )

The primary way to interact with our application is by using the call method, passing the method we want to call and the arguments it expects as keyword arguments.

    app_client1.call(nicknames.set_nick, nick="first")

If multiple app calls are required in the same atomic group, the ApplicationClient also allows composing a group of calls using the add_method_call method which uses an AtomicTransactionComposer to build the group.

If there are multiple signers, or you want to re-use some suggested parameters, the prepare method may be called with the different arguments and a copy of the client is returned with the updated parameters.

    app_client2 = app_client1.prepare(signer=acct2.signer)

Full Example

class beaker.client.ApplicationClient[source]
__init__(client: AlgodClient, app: algokit_utils.application_specification.ApplicationSpecification | str | pathlib.Path | beaker.application.Application, *, app_id: int = 0, signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, sender: str | None = None, suggested_params: algosdk.transaction.SuggestedParams | None = None)[source]
clear_state(sender: str | None = None, signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, suggested_params: algosdk.transaction.SuggestedParams | None = None, **kwargs: Any) str[source]

Submits a signed ApplicationCallTransaction with OnComplete set to ClearState

close_out(sender: str | None = None, signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, suggested_params: algosdk.transaction.SuggestedParams | None = None, **kwargs: Any) str[source]

Submits a signed ApplicationCallTransaction with OnComplete set to CloseOut

create(sender: str | None = None, signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, suggested_params: algosdk.transaction.SuggestedParams | None = None, on_complete: OnComplete = OnComplete.NoOpOC, extra_pages: int | None = None, **kwargs: Any) tuple[int, str, str][source]

Submits a signed ApplicationCallTransaction with application id == 0 and the schema and source from the Application passed

delete(sender: str | None = None, signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, suggested_params: algosdk.transaction.SuggestedParams | None = None, **kwargs: Any) str[source]

Submits a signed ApplicationCallTransaction with OnComplete set to DeleteApplication

fund(amt: int, addr: str | None = None) str[source]

convenience method to pay the address passed, defaults to paying the app address for this client from the current signer

get_application_account_info() dict[str, Any][source]

gets the account info for the application account

opt_in(sender: str | None = None, signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, suggested_params: algosdk.transaction.SuggestedParams | None = None, **kwargs: Any) str[source]

Submits a signed ApplicationCallTransaction with OnComplete set to OptIn

prepare(signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, sender: str | None = None, app_id: int | None = None) ApplicationClient[source]

makes a copy of the current ApplicationClient and the fields passed

update(sender: str | None = None, signer: algosdk.atomic_transaction_composer.TransactionSigner | None = None, suggested_params: algosdk.transaction.SuggestedParams | None = None, **kwargs: Any) str[source]

Submits a signed ApplicationCallTransaction with OnComplete set to UpdateApplication and source from the Application passed

Full Example

from algokit_utils import LogicError

from beaker import client, consts, localnet
from beaker.client import ApplicationClient

from examples.client import nicknames


def main() -> None:
    # Set up accounts we'll use
    accts = localnet.get_accounts()

    acct1 = accts.pop()
    acct2 = accts.pop()

    # Create Application client
    app_client1 = client.ApplicationClient(
        client=localnet.get_algod_client(), app=nicknames.app, signer=acct1.signer
    )

    # Create the app on-chain (uses signer1)
    app_id, app_addr, txid = app_client1.create()
    print(f"Created app with id {app_id} and address {app_addr} in transaction {txid}")

    print(f"Current app state: {app_client1.get_global_state()}")
    # Fund the app account with 1 algo
    app_client1.fund(1 * consts.algo)

    # Try calling set nickname from both accounts after opting in
    app_client1.opt_in()
    app_client1.call(nicknames.set_nick, nick="first")

    print(f"Current app state: {app_client1.get_global_state()}")
    # Create copies of the app client with specific signer, _after_ we've created and set the app id
    app_client2 = app_client1.prepare(signer=acct2.signer)
    print(f"Current app state: {app_client1.get_global_state()}")

    # Try calling without opting in
    try:
        app_client2.call(nicknames.set_nick, nick="second")
    except LogicError as e:
        print(f"\n{e}\n")

    app_client2.opt_in()
    app_client2.call(nicknames.set_nick, nick="second")

    # Get the local state for each account
    print(app_client1.get_local_state())
    print(app_client2.get_local_state())

    # Get the global state
    print(f"Current app state: {app_client1.get_global_state()}")

    try:
        app_client2.call(nicknames.set_manager, new_manager=acct2.address)
        print("Shouldn't get here")
    except LogicError as e:
        print("Failed as expected, only addr1 should be authorized to set the manager")
        print(f"\n{e}\n")

    # Have addr1 set the manager to addr2
    app_client1.call(nicknames.set_manager, new_manager=acct2.address)
    print(f"Current app state: {app_client1.get_global_state()}")

    print(app_client1.get_global_state())
    # and back
    app_client2.call(nicknames.set_manager, new_manager=acct1.address)
    print(f"Current app state: {app_client1.get_global_state()}")

    # Create a new client that just sets the app id we wish to interact with
    app_client3 = ApplicationClient(
        client=localnet.get_algod_client(),
        app=nicknames.app,
        signer=acct1.signer,
        app_id=app_client1.app_id,
    )

    try:
        app_client1.close_out()
        app_client2.close_out()
        app_client3.delete()
    except Exception as e:
        print(e)


if __name__ == "__main__":
    main()