Decorator Parameters

Beaker Application decorators accept parameters that apply configurations to the methods they decorate.

Authorization

Often, we would like to restrict the accounts that may call certain methods.

Lets add a parameter to the external to allow only the app creator to call this method.

from beaker.decorators import Authorize

#...

@app.external(authorize=Authorize.only(Global.creator_address()))
def increment(*, output: abi.Uint64):
    return Seq(
        self.counter.set(self.counter + Int(1)),
        output.set(self.counter)
    )

The authorize parameter may be any Subroutine that accepts a sender as its argument and returns an integer interpreted as true/false.

Now lets write a new method to allow any account that is opted in to call it:

from beaker.decorators import Authorize

# ...

@app.external(authorize=Authorize.opted_in())
def vote(approve: abi.Bool):
    # ...

This authorize check will cause the contract call to fail if the sender has not opted in to the app. Another app id may also be passed in case you want to check if the Sender is opted in to a different application.

The pre-defined Authorized checks are:

static Authorize.only(addr: Expr) Callable[[Expr], Expr][source]

require that the sender of the app call match exactly the address passed

static Authorize.holds_token(asset_id: Expr) Callable[[Expr], Expr][source]

require that the sender of the app call holds >0 of the asset id passed

static Authorize.opted_in(app_id: ~pyteal.Expr = <pyteal.Global object>) Callable[[Expr], Expr][source]

require that the sender of the app call has already opted-in to a given app id

But we can define our own

from pyteal import Subroutine
from beaker.consts import Algos

@Subroutine(TealType.uint64)
def is_whale(acct: Expr):
    # Only allow accounts with 1mm algos
    return Balance(acct)>Algos(1_000_000)

# ...

@app.external(authorize=is_whale)
def greet(*, output: abi.String):
    return output.set("hello whale")

Read Only

Methods that are meant to only produce information, having no side effects, should be flagged as read only.

See ARC22 for more details.

class ROAppState:
    count = ApplicationStateValue(stack_type=TealType.uint64)

app = Application("CoolApp", state=ROAppState())

@app.external(read_only=True)
def get_count(id_of_thing: abi.Uint8, *, output: abi.Uint64):
    return output.set(app.state.count)

On Complete

If a method expects the ApplicationCallTransaction to have a certain OnComplete other than NoOp, one of the other OnComplete decorators may be used instead of external with a method config set.

class beaker.application.Application[source]
external(fn: Callable[[...], Expr], /) ABIReturnSubroutine[source]
external(*, method_config: pyteal.MethodConfig | dict[Literal['no_op', 'opt_in', 'close_out', 'update_application', 'delete_application'], pyteal.CallConfig] | None = None, name: str | None = None, authorize: collections.abc.Callable[[pyteal.Expr], pyteal.Expr] | pyteal.SubroutineFnWrapper | None = None, read_only: bool = False, override: bool | None = False) Callable[[Callable[[...], Expr]], ABIReturnSubroutine]
external(*, method_config: pyteal.MethodConfig | dict[Literal['no_op', 'opt_in', 'close_out', 'update_application', 'delete_application'], pyteal.CallConfig] | None = None, name: str | None = None, authorize: collections.abc.Callable[[pyteal.Expr], pyteal.Expr] | pyteal.SubroutineFnWrapper | None = None, bare: Literal[False], read_only: bool = False, override: bool | None = False) Callable[[Callable[[...], Expr]], ABIReturnSubroutine]
external(*, method_config: pyteal.MethodConfig | dict[Literal['no_op', 'opt_in', 'close_out', 'update_application', 'delete_application'], pyteal.CallConfig], name: str | None = None, authorize: collections.abc.Callable[[pyteal.Expr], pyteal.Expr] | pyteal.SubroutineFnWrapper | None = None, bare: Literal[True], override: bool | None = False) Callable[[Callable[[], Expr]], SubroutineFnWrapper]
external(*, method_config: pyteal.MethodConfig | dict[Literal['no_op', 'opt_in', 'close_out', 'update_application', 'delete_application'], pyteal.CallConfig] | None = None, name: str | None = None, authorize: collections.abc.Callable[[pyteal.Expr], pyteal.Expr] | pyteal.SubroutineFnWrapper | None = None, bare: bool, read_only: bool = False, override: bool | None = False) collections.abc.Callable[[collections.abc.Callable[[...], pyteal.Expr]], pyteal.ABIReturnSubroutine] | collections.abc.Callable[[collections.abc.Callable[[], pyteal.Expr]], pyteal.SubroutineFnWrapper]

Add the method decorated to be handled as an ABI method for the Application

Parameters
  • fn – The function being wrapped.

  • method_config – <TODO>

  • name – Name of ABI method. If not set, name of the python method will be used. Useful for method overriding.

  • authorize – a subroutine with input of Txn.sender() and output uint64 interpreted as allowed if the output>0.

  • bare

  • read_only – Mark a method as callable with no fee using dryrun or simulate

  • override

Returns

An ABIReturnSubroutine or SubroutineFnWrapper

The ARC4 spec allows applications to define externals for bare methods, that is, methods with no application arguments.

Routing for bare methods is based on the transaction’s OnComplete and whether or not it’s a create transaction, not based on the method selector as non-bare methods.

The same handlers described above will also work for bare method calls but multiple OnComplete values can be handled with the bare_external decorator.

Multiple Bare externals

If a method requires handling multiple OnComplete actions, use Application.external with the parameter bare=True