Global Storage | Algorand Developer Portal

Global Storage

Global state is associated with the app itself. Global storage is a feature in Algorand that allows smart contracts to persistently store key-value pairs in a globally accessible state. This guide provides comprehensive information on how to allocate, read, write, and manipulate global storage within smart contracts.

Manipulating Global State Storage

Smart contracts can create, update, and delete values in global state using TEAL (Transaction Execution Approval Language) opcodes. The number of values that can be written is limited by the initial configuration set during smart contract creation. State is represented as key-value pairs, where keys are stored as byte slices (byte-array values), and values can be stored as either byte slices or uint64 values. TEAL provides several opcodes for facilitating reading and writing to state, including app_global_put, app_global_get, app_global_get_ex.

Allocation

Global storage can include between 0 and 64 key/value pairs and a total of 8K of memory to share among them. The amount of global storage is allocated in key/value units and determined at contract creation, which cannot be edited later. The contract creator address is responsible for funding the global storage by an increase to their minimum balance requirement.

public globalInt = GlobalState<uint64>({ initialValue: Uint64(50) }) // UInt64 with default value

public globalIntNoDefault = GlobalState<uint64>() // UInt64 with no default value

public globalBytes = GlobalState<bytes>({ initialValue: Bytes('Silvio') }) // Bytes with default value

public globalString = GlobalState<string>({ initialValue: 'Micali' }) // Bytes with default value

public globalBool = GlobalState({ initialValue: true }) // Bool with default value

public globalAccount = GlobalState<Account>() // Address with no default value
def __init__(self) -> None:

self.global_int_full = GlobalState(UInt64(50))  # UInt64 with default value = 50

self.global_int_simplified = UInt64(

10

)  # UInt64 simplified with default value = 10

self.global_int_no_default = GlobalState(UInt64)  # UInt64 with no default value

# example: INIT_BYTES

self.global_bytes_full = GlobalState(

Bytes(b"Hello")

)  # Bytes with default value = bytes(Hello)

self.global_bytes_simplified = Bytes(

b"Hello"

)  # Bytes simplified with default value = bytes(Hello)

self.global_bytes_no_default = GlobalState(Bytes)  # Bytes with no default value

# example: INIT_BYTES

self.global_bool_simplified = True  # Bool

self.global_bool_no_default = GlobalState(bool)  # Bool

self.global_asset = GlobalState(Asset)  # Asset

self.global_application = GlobalState(Application)  # Application

self.global_account = GlobalState(Account)  # Account
#pragma version 10

__init__:

proto 0 0

byte "global_bytes_full"

byte 0x48656c6c6f

app_global_put

byte "global_bytes_simplified"

byte 0x48656c6c6f

app_global_put

retsub

Reading from Global State

The global storage of a smart contract can be read by any application call that specifies the contract’s application ID in its foreign apps array. The key-value pairs in global storage can be read on-chain directly, or off-chain using SDKs, APIs, and the goal CLI. Only the smart contract itself can write to its own global storage.

TEAL provides opcodes to read global state values for the current smart contract. The app_global_get opcode retrieves values from the current contract’s global storage, respectively. The app_global_get_ex opcode returns two values on the stack: a boolean indicating whether the value was found, and the actual value if it exists.

These _ex opcodes allow reading global states from other accounts and smart contracts, as long as the account and contract are included in the accounts and applications arrays. Branching logic is typically used after calling the _ex opcodes to handle cases where the value is found or not found.

/**
 * Reads and returns all global state values from the contract
 * @returns A tuple containing [globalInt, globalIntNoDefault, globalBytes, globalString, globalBool, globalAccount]
 * where each value corresponds to the current state of the respective global variable
 */
public readGlobalState(): [uint64, uint64, bytes, string, boolean, arc4.Address] {
  // Convert Account reference type to native Address type for return value
  const accountAddress = new arc4.Address(this.globalAccount.value)
  return [
    this.globalInt.value,
    this.globalIntNoDefault.value,
    this.globalBytes.value,
    this.globalString.value,
    this.globalBool.value,
    accountAddress,
  ]
}
@arc4.abimethod
def get_global_state(self) -> UInt64:
    return self.global_int_full.get(default=UInt64(0))

@arc4.abimethod
def maybe_global_state(self) -> tuple[UInt64, bool]:
    int_value, int_exists = self.global_int_full.maybe()  # uint64
    if not int_exists:
        int_value = UInt64(0)
    return int_value, int_exists

@arc4.abimethod
def get_global_state_example(self) -> bool:
    assert self.global_int_full.get(default=UInt64(0)) == 50  # uint64
    assert self.global_int_simplified == UInt64(10)  # get function cannot be used
    assert self.global_int_no_default.get(default=UInt64(0)) == 0
    assert self.global_bytes_full.get(Bytes(b"default")) == b"Hello"  # byte
    return True
#pragma version 10

get_global_state:
    proto 0 1
    int 0
    byte "global_int_full"
    app_global_get_ex
    int 0
    cover 2
    select
    retsub

maybe_global_state:
    proto 0 2
    int 0
    byte "global_int_full"
    app_global_get_ex
    dup
    uncover 2
    swap
    bnz maybe_global_state_after_if_else@2
    int 0
    frame_bury 1
maybe_global_state_after_if_else@2:
    frame_dig 1
    frame_dig 0
    uncover 3
    uncover 3
    retsub

Interpretation:

The app_global_get_ex is used to read not only the global state of the current contract but any contract that is in the applications array. To access these foreign apps, they must be passed in with the application call.

Writing to Global State

Can only be written by smart contract. To write to global state, use the app_global_put opcode.

/**
 * Updates multiple global state values
 * @param valueBytes New value for globalBytes
 * @param valueBool New value for globalBool
 * @param valueAccount New value for globalAccount
 */
public writeGlobalState(valueString: string, valueBool: boolean, valueAccount: Account): void {
  this.globalString.value = valueString
  this.globalBool.value = valueBool
  this.globalAccount.value = valueAccount
  assert(this.globalString.value === valueString)
  assert(this.globalBool.value === valueBool)
  assert(this.globalAccount.value === valueAccount)
}
@arc4.abimethod
def set_global_state(self, value: Bytes) -> None:
    self.global_bytes_full.value = value
#pragma version 10

set_global_state:
    proto 1 0
    byte "global_bytes_full"
    frame_dig -1
    app_global_put
    retsub

Deleting Global State

Global storage is deleted when the corresponding smart contract is deleted. However, the smart contract can clear the contents of its global storage without affecting the minimum balance requirement.

public deleteGlobalState(): boolean {
  this.globalInt.delete()
  return true
}
@arc4.abimethod
def del_global_state(self) -> bool:
    del self.global_int_full.value
    return True
#pragma version 10

del_global_state:
    proto 0 1
    byte "global_int_full"
    app_global_del
    int 1
    retsub

Summary of Global State Operations

For manipulating global storage data like reading, writing, deleting and checking if exists:

TEAL: Different opcodes can be used

Function Description
app_global_get Get global data for the current app
app_global_get_ex Get global data for other app
app_global_put Set global data to the current app
app_global_del Delete global data from the current app
app_global_get_ex Check if global data exists for the current app
app_global_get_ex Check if global data exists for the other app

Different functions of globalState class can be used. The detailed api reference can be found here

Function Description
GlobalState(type_) Initialize a global state with the specified data type
get(default) Get data or a default value if not found
maybe() Get data and a boolean indicating if it exists