Your First Application

Use the guide below to deploy a "hello world" Algorand application, a stateful smart contract using the Transaction Execution Approval Language (TEAL). This requires setting up your workspace and connecting to a node. This guide covers drafting the smart contract code, deploying to the network, interacting with and removing the application.

This is a very basic application which implements a counter. Each time the application is called, the counter value is incremented.

Info

Algorand Smart Contracts (ASC1) are deployed as either "Stateful" or "Stateless" programs. Both use the Transaction Execution Approval Language (TEAL), but the available OpCodes and, therefore their function, vary by type. This guide covers only stateful smart contracts; learn more about stateless smart contracts.

Stateful Application Primer

The Overview of Algorand Stateful Smart Contracts should be consulted for details of the concepts covered in this document. Here is a brief primer to get started.

The term "stateful" means the application is able to store information or "maintain state" within the ledger. The information ("data") is structured into key/value pairs. The Transaction Execution Approval Language (TEAL) defines the available OpCodes for use during program execution. Application Call Transactions are used to interact with the application and may include arguments (additional data) which are evaluated by the program at run-time. Every program execution must complete with a single non-zero unit64 value remaining on the stack to be valid and thus commit all state changes to the ledger.

Application Components

Programs

Every Algorand stateful application is comprised of at least two programs: approval and clear state.

Approval Program

The approval program is the main application logic for the smart contract.

Clear State Program

The clear state program is used to retire an application, its global state and remove local state from the user’s account.

State Storage Locations

Applications have access to three storage locations for stateful data: global, local and external.

Global

Global data are stored within the application itself. The program will read from and write to its global storage the same regardless of which account submitted the application call transaction.

Local

Each program may read from and write to local storage within the account object of the "calling account". This allows an application developer to store user specific data and vary program execution logic at run-time per user.

External

Each program may read both the global and local state storage locations for limited number of other external programs and accounts. For more information on external programs and accounts see the stateful smart contract documentation.

State Data

Both global and local data are stored as key/value pairs, where the key is bytes and the value may be either a bytes or uint64.

{ "key": {
    "type": <1 || 2>,
    "value": <[]byte || uint64>
    }
}

Program Execution

Application users will

Application Call Transaction

Each application call transaction from a user account instructs the approval application to execute. Arguments may be specified within the call transaction and consumed by the program during execution.

State Access and Updates

TEAL provides OpCodes allowing the program to get (read) and put (write) data within state storage locations.

get

Programs may implicitly read their own global storage and the local storage of the account submitting the application call transaction.

get_external

Reading from global and local storage of an external program or account is allowed by explicitly passing the address as an argument within the application call transaction. Programs may read from global storage of up to two (2) external programs. Additionally, programs may read from local storage of up to four (4) external accounts.

put

Writing data is restricted to global storage of the "called" program and the local storage of the "calling" account, both specified within the application call transaction (note: external locations may only be read from).

Draft Application Code

Approval Program

Create a new file named approval_program.teal and add the following code:

#pragma version 2

// read global state
byte "counter"
dup
app_global_get

// increment the value
int 1
+

// store to scratch space
dup
store 0

// update global state
app_global_put

// load return value as approval
load 0
return
Define TEAL Version

The approval program is holding stateful data so if must instruct the TEAL interpreter to use "version 2" OpCodes during execution.

  • #pragma version 2
Read from Global State

This program has a single global key/value pair to store the number of times the program was called. The key is the string "counter" and the value will be defined as an integer when the application is created below.

  • byte "counter" places the bytes representing the string "counter" on the stack
  • dup duplicates that, so now "counter" is on the stack twice
  • app_global_get pops the top of the stack, looks within global for the key "counter" and places the value back on top of the stack.
Perform Program Logic

The program now has the current value for "counter" and will increment this value. A copy of this value will be stored in scratch space for use below. Note: scratch space is accessible by the program only during run-time and cannot persist any values after execution completes.

  • int 1 places the integer 1 on the stack
  • + pops two elements from the stack (the value for "counter" and int 1), sums them, and places the result back on top of the stack
  • dup duplicates the to top most value on the stack
  • store 0 pops the duplicate value from the stack and moves it to the first position within scratch space.
Write to Global State

Now the program will store the incremented value into global storage at key "counter". This value will persist on the ledger if the program completes successfully.

  • app_global_put pops two elements from the stack (key "counter" from line 5 and value calculated in line 11) and stores into global the value for key.
Approval

At this point the stack is empty. A valid approval program must complete with a single non-zero uint64 value remaining on the stack. If the value is 0x00 (false) the program will fail, all state transitions will be discarded, and the calling transaction will also fail. The scratch space still holds a duplicate copy the counter value. Loading that ensures the program will complete successfully.

  • load 0 copies the first value from scratch space and places it on top of the stack
  • return use last value on stack as success value; end

Clear State Program

The clear state program is used by the application to clear the global and local state storage locations for the application and accounts. The "hello world" application does not evaluate any conditions and simply approves the call.

Create a new file named clear_state_program.teal and add the following code:

#pragma version 2
// This program clears program state

int 1

  • #pragma version 2 instructs the TEAL interpreter to use "version 2" OpCodes during execution. This is required for stateful smart contract applications
  • int 1 places the integer 1 on the stack, signaling approval.

Deploy New Application

With the two program files completed, the application may be created on the blockchain using the bash script below. The script uses goal and requires a funded account $ADDR_CREATOR to pay the transaction fee. The flags are rather descriptive. Notice that only $GLOBAL_INTS has a value of 1 and is used to allocate a single global state storage element using the --global-ints flag.

export ADDR_CREATOR="YOURACCOUNTIDENTIFIERGOESHERE"

export TEAL_APPROVAL_PROG="approval_program.teal"
export TEAL_CLEAR_PROG="clear_state_program.teal"

export GLOBAL_BYTESLICES=0
export GLOBAL_INTS=1
export LOCAL_BYTESLICES=0
export LOCAL_INTS=0

goal app create --creator $ADDR_CREATOR \
                --approval-prog $TEAL_APPROVAL_PROG \
                --clear-prog $TEAL_CLEAR_PROG \
                --global-byteslices $GLOBAL_BYTESLICES \
                --global-ints $GLOBAL_INTS \
                --local-byteslices $LOCAL_BYTESLICES \
                --local-ints $LOCAL_INTS 

Results:

Attempting to create app (approval size 25, hash L4N6WP75R2G6M3TMLWSLA5S4PNHQIMGYTFMSOWNU6Q6X3R5LOU5A; clear size 5, hash YOE6C22GHCTKAN3HU4SE5PGIPN5UKXAJTXCQUPJ3KKF5HOAH646A)
Issued transaction from account TZIWJ3BKGLGTFDA4YAZPHIXGIEOVA5XLHWOCINUQY2PPHZ6XVFGKGMWVGA, txid L7A7HHCLWV3KBUHKMSVEVBMRPWRLHTXEUVYVVLFKWUDOWJEAWLTQ (fee 1000)
Transaction L7A7HHCLWV3KBUHKMSVEVBMRPWRLHTXEUVYVVLFKWUDOWJEAWLTQ still pending as of round 260564
Transaction L7A7HHCLWV3KBUHKMSVEVBMRPWRLHTXEUVYVVLFKWUDOWJEAWLTQ committed in round 260566
Created app with app index 33

Application ID 33 was created above. Check yours using:

$ goal account dump --address $ADDR_CREATOR
Results:
{
  "addr": "TZIWJ3BKGLGTFDA4YAZPHIXGIEOVA5XLHWOCINUQY2PPHZ6XVFGKGMWVGA",
  "algo": 308000,
  "appp": {
    "33": {
      "approv": "AiABASYBB2NvdW50ZXIoSWQiCEk1AGc0AA==",
      "clearp": "AiABASI=",
      "gs": {
        "counter": {
          "tt": 2,
          "ui": 1
        [...]
}

Notice the "appp" section has "33" which is the application ID found within the creator's account. Export your specific value for use within future scripts using:

export APP_ID=<your_app_id>

Another way to view information about an application is with the following goal command:

$ goal app info --app-id $APP_ID

Results:

Application ID:        33
Creator:               WJ4ZVEUUWRGR6PFOFR6XK3B5BQ4ZNHFVLNOPBZS53DE7AJJB6B7IBXQ3OQ
Approval hash:         W6L5UMEQICBJJ5QZAN3YMXISJWK3I6SHTOYMTA3EQS6FZBXHMTJV3SXZUA
Clear hash:            YOE6C22GHCTKAN3HU4SE5PGIPN5UKXAJTXCQUPJ3KKF5HOAH646MKKCPDA
Max global byteslices: 0
Max global integers:   1
Max local byteslices:  0
Max local integers:    0

Notice the values for the Max state storage locations match those specified when the app was created above. These values may not be altered after creation.

Application Calls

There are a number of application calls available to interact with the application.

OptIn to Application

Most applications require each user OptIn prior to interacting with the application. However, this "hello world" application did not allocate any local state when created so it will not require an OptIn application call by any accounts.

Read State

Both global and local state may be read by anyone accessing the ledger. The following displays the current state of your application:

goal app read --global --app-id $APP_ID
Result:
{
  "counter": {
    "tt": 2,
    "ui": 1
}
Here the single global key/value pair for your application is displayed. The key "counter" was assigned the type "tt" of integer at creation (1 for bytes, 2 for uint64). The current integer value "ui" is 1. The approval program executed once during creation.

Call Application

Calling the application again requires knowing the application id and paying the transaction fee. Anyone on the network may call this application, so modifying the --from flag will succeed if that account is funded.

goal app call --app-id $APP_ID --from $ADDR_CREATOR

Check the application state again:

goal app read --global --app-id $APP_ID
Result:
{
  "counter": {
    "tt": 2,
    "ui": 2
}
The "hello world" application has been called again as observed by the incremented value "ui" for key "counter". Continue making calls and reading the updated state.

Retire the Application

Each account is limited to creating or opting into a total of 10 applications. Removing an application may be accomplished using:

goal app delete --app-id $APP_ID --from $ADDR_CREATOR
Results for both goal account dump and goal app read will not include the application. You may reuse the existing program source files to create the application again. However, that will result in a new application id and a reset of your global "counter" state back to 1.