App Manager
Description
Section titled “Description”This example demonstrates the AppManager functionality for querying application information, state, box storage, and TEAL compilation:
- algorand.app.getById() to fetch application information
- algorand.app.getGlobalState() to read global state
- algorand.app.getLocalState() to read account’s local state
- algorand.app.getBoxNames() to list all box names for an app
- algorand.app.getBoxValue() to read a specific box value
- algorand.app.getBoxValues() to read multiple box values
- algorand.app.getBoxValuesFromABIType() to decode typed box values
- algorand.app.compileTeal() to compile TEAL source code
- algorand.app.compileTealTemplate() to compile TEAL with template variables
Prerequisites
Section titled “Prerequisites”- LocalNet running (via
algokit localnet start)
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example algorand_client/12-app-manager.ts/** * Example: App Manager * * This example demonstrates the AppManager functionality for querying * application information, state, box storage, and TEAL compilation: * - algorand.app.getById() to fetch application information * - algorand.app.getGlobalState() to read global state * - algorand.app.getLocalState() to read account's local state * - algorand.app.getBoxNames() to list all box names for an app * - algorand.app.getBoxValue() to read a specific box value * - algorand.app.getBoxValues() to read multiple box values * - algorand.app.getBoxValuesFromABIType() to decode typed box values * - algorand.app.compileTeal() to compile TEAL source code * - algorand.app.compileTealTemplate() to compile TEAL with template variables * * Prerequisites: * - LocalNet running (via `algokit localnet start`) */
import { ABIType } from '@algorandfoundation/algokit-utils/abi'import { OnApplicationComplete } from '@algorandfoundation/algokit-utils/transact'import { AlgorandClient, algo } from '@algorandfoundation/algokit-utils'import { loadTealSource, printError, printHeader, printInfo, printStep, printSuccess, shortenAddress } from '../shared/utils.js'
// ============================================================================// TEAL Programs - loaded from shared artifacts// ============================================================================
// A stateful app that supports global state, local state, and box storageconst APPROVAL_PROGRAM = loadTealSource('approval-box-storage.teal')
// Clear state program (must always approve)const CLEAR_STATE_PROGRAM = loadTealSource('clear-state-approve.teal')
// A TEAL template with replaceable parametersconst TEAL_TEMPLATE = loadTealSource('teal-template-basic.teal')
// A TEAL template with AlgoKit deploy-time control parametersconst ALGOKIT_TEMPLATE = loadTealSource('teal-template-deploy-control.teal')
async function main() { printHeader('App Manager Example')
// Initialize client and verify LocalNet is running const algorand = AlgorandClient.defaultLocalNet()
try { await algorand.client.algod.status() printSuccess('Connected to LocalNet') } catch (error) { printError(`Failed to connect to LocalNet: ${error instanceof Error ? error.message : String(error)}`) printInfo('Make sure LocalNet is running (e.g., algokit localnet start)') return }
// Step 1: Create and fund test accounts printStep(1, 'Create and fund test accounts') printInfo('Creating accounts for app manager demonstrations')
const creator = algorand.account.random() const user = algorand.account.random()
printInfo(`\nCreated accounts:`) printInfo(` Creator: ${shortenAddress(creator.addr.toString())}`) printInfo(` User: ${shortenAddress(user.addr.toString())}`)
// Fund accounts await algorand.account.ensureFundedFromEnvironment(creator.addr, algo(20)) await algorand.account.ensureFundedFromEnvironment(user.addr, algo(10))
printSuccess('Created and funded test accounts')
// Step 2: Deploy a test application with state and boxes printStep(2, 'Deploy a test application with state and boxes') printInfo('Creating an app with global state, local state schema, and box storage')
const createResult = await algorand.send.appCreate({ sender: creator.addr, approvalProgram: APPROVAL_PROGRAM, clearStateProgram: CLEAR_STATE_PROGRAM, schema: { globalInts: 1, // counter globalByteSlices: 2, // message, creator localInts: 2, // user_score, opted_in_round localByteSlices: 0, }, })
const appId = createResult.appId const appAddress = createResult.appAddress
printInfo(`\nApplication created:`) printInfo(` App ID: ${appId}`) printInfo(` App Address: ${shortenAddress(appAddress.toString())}`) printInfo(` Transaction ID: ${createResult.txIds[0]}`)
printSuccess('Test application deployed')
// Step 3: Demonstrate algorand.app.getById() printStep(3, 'Demonstrate algorand.app.getById() to fetch application information') printInfo('Fetching complete application information by ID')
const appInfo = await algorand.app.getById(appId)
printInfo(`\nApplication information:`) printInfo(` App ID: ${appInfo.appId}`) printInfo(` App Address: ${shortenAddress(appInfo.appAddress.toString())}`) printInfo(` Creator: ${shortenAddress(appInfo.creator.toString())}`) printInfo(` Global State Schema:`) printInfo(` globalInts: ${appInfo.globalInts}`) printInfo(` globalByteSlices: ${appInfo.globalByteSlices}`) printInfo(` Local State Schema:`) printInfo(` localInts: ${appInfo.localInts}`) printInfo(` localByteSlices: ${appInfo.localByteSlices}`) printInfo(` Extra Program Pages: ${appInfo.extraProgramPages ?? 0}`) printInfo(` Approval Program: ${appInfo.approvalProgram.length} bytes`) printInfo(` Clear State Program: ${appInfo.clearStateProgram.length} bytes`)
printSuccess('Application information retrieved')
// Step 4: Demonstrate algorand.app.getGlobalState() printStep(4, 'Demonstrate algorand.app.getGlobalState() to read global state') printInfo('Reading all global state key-value pairs')
const globalState = await algorand.app.getGlobalState(appId)
printInfo(`\nGlobal state entries:`) for (const [key, stateValue] of Object.entries(globalState)) { if ('valueRaw' in stateValue) { // Byte slice value printInfo(` "${key}": "${stateValue.value}" (bytes)`) } else { // Integer value printInfo(` "${key}": ${stateValue.value} (uint64)`) } }
// Increment counter a few times printInfo(`\nIncrementing counter...`) for (let i = 0; i < 3; i++) { await algorand.send.appCall({ sender: creator.addr, appId: appId, args: [new TextEncoder().encode('increment')], note: `increment-${i}`, // Unique note to differentiate transactions }) }
const updatedGlobalState = await algorand.app.getGlobalState(appId) printInfo(` Counter after 3 increments: ${updatedGlobalState['counter']?.value ?? 0}`)
printSuccess('Global state read successfully')
// Step 5: Demonstrate algorand.app.getLocalState() printStep(5, 'Demonstrate algorand.app.getLocalState() to read local state') printInfo('User must opt in first to have local state')
// Opt user into the app await algorand.send.appCall({ sender: user.addr, appId: appId, onComplete: OnApplicationComplete.OptIn, }) printInfo('User opted in to the application')
const localState = await algorand.app.getLocalState(appId, user.addr)
printInfo(`\nUser's local state:`) for (const [key, stateValue] of Object.entries(localState)) { if ('valueRaw' in stateValue) { printInfo(` "${key}": "${stateValue.value}" (bytes)`) } else { printInfo(` "${key}": ${stateValue.value} (uint64)`) } }
printInfo(`\nLocal state was initialized on opt-in with:`) printInfo(` user_score: ${localState['user_score']?.value ?? 0} (initial value)`) printInfo(` opted_in_round: ${localState['opted_in_round']?.value ?? 0} (round when opted in)`)
printSuccess('Local state read successfully')
// Step 6: Create boxes and demonstrate box operations printStep(6, 'Demonstrate box storage operations') printInfo('Creating boxes to store application data')
// Fund the app account for box storage (boxes require MBR) await algorand.send.payment({ sender: creator.addr, receiver: appAddress, amount: algo(1), // Fund for box storage MBR })
// Create multiple boxes with different content const boxData = [ { name: 'user_data', value: 'Alice:100:premium' }, { name: 'config', value: '{"version":1,"enabled":true}' }, { name: 'scores', value: 'high:9999,low:1' }, ]
for (const box of boxData) { await algorand.send.appCall({ sender: creator.addr, appId: appId, args: [ new TextEncoder().encode('set_box'), new TextEncoder().encode(box.name), new TextEncoder().encode(box.value), ], boxReferences: [{ appId: 0n, name: new TextEncoder().encode(box.name) }], }) printInfo(` Created box "${box.name}" with ${box.value.length} bytes`) }
printSuccess('Boxes created')
// Step 7: Demonstrate algorand.app.getBoxNames() printStep(7, 'Demonstrate algorand.app.getBoxNames() to list all boxes') printInfo('Retrieving all box names for the application')
const boxNames = await algorand.app.getBoxNames(appId)
printInfo(`\nApplication has ${boxNames.length} boxes:`) for (const boxName of boxNames) { printInfo(` Name: "${boxName.name}"`) printInfo(` Raw bytes: ${boxName.nameRaw.length} bytes`) printInfo(` Base64: ${boxName.nameBase64}`) }
printSuccess('Box names retrieved')
// Step 8: Demonstrate algorand.app.getBoxValue() printStep(8, 'Demonstrate algorand.app.getBoxValue() to read a single box') printInfo('Reading the value of a specific box by name')
const boxValue = await algorand.app.getBoxValue(appId, 'user_data')
printInfo(`\nBox "user_data" value:`) printInfo(` Raw bytes: ${boxValue.length} bytes`) printInfo(` As string: "${new TextDecoder().decode(boxValue)}"`)
// Read another box const configValue = await algorand.app.getBoxValue(appId, 'config') printInfo(`\nBox "config" value:`) printInfo(` As string: "${new TextDecoder().decode(configValue)}"`)
printSuccess('Box value retrieved')
// Step 9: Demonstrate algorand.app.getBoxValues() printStep(9, 'Demonstrate algorand.app.getBoxValues() to read multiple boxes at once') printInfo('Reading multiple box values in a single call')
const allBoxValues = await algorand.app.getBoxValues(appId, ['user_data', 'config', 'scores'])
printInfo(`\nRetrieved ${allBoxValues.length} box values:`) const boxNamesArray = ['user_data', 'config', 'scores'] for (let i = 0; i < allBoxValues.length; i++) { printInfo(` "${boxNamesArray[i]}": "${new TextDecoder().decode(allBoxValues[i])}"`) }
printSuccess('Multiple box values retrieved')
// Step 10: Demonstrate algorand.app.getBoxValuesFromABIType() printStep(10, 'Demonstrate algorand.app.getBoxValuesFromABIType() for typed box decoding') printInfo('Creating boxes with ABI-encoded values and decoding them')
// Create a box with ABI-encoded uint64 value const abiType = ABIType.from('uint64') const encodedValue = abiType.encode(BigInt(42))
await algorand.send.appCall({ sender: creator.addr, appId: appId, args: [ new TextEncoder().encode('set_box'), new TextEncoder().encode('abi_number'), encodedValue, ], boxReferences: [{ appId: 0n, name: new TextEncoder().encode('abi_number') }], }) printInfo('Created box "abi_number" with ABI-encoded uint64 value')
// Read and decode the ABI value const decodedValues = await algorand.app.getBoxValuesFromABIType({ appId: appId, boxNames: ['abi_number'], type: abiType, })
printInfo(`\nDecoded ABI values:`) printInfo(` "abi_number" (uint64): ${decodedValues[0]}`)
// Create boxes with ABI-encoded string values const stringType = ABIType.from('string') const encodedString = stringType.encode('Hello, ABI!')
await algorand.send.appCall({ sender: creator.addr, appId: appId, args: [ new TextEncoder().encode('set_box'), new TextEncoder().encode('abi_string'), encodedString, ], boxReferences: [{ appId: 0n, name: new TextEncoder().encode('abi_string') }], })
const decodedStrings = await algorand.app.getBoxValuesFromABIType({ appId: appId, boxNames: ['abi_string'], type: stringType, })
printInfo(` "abi_string" (string): "${decodedStrings[0]}"`)
printSuccess('ABI-typed box values decoded')
// Step 11: Demonstrate algorand.app.compileTeal() printStep(11, 'Demonstrate algorand.app.compileTeal() to compile TEAL source') printInfo('Compiling TEAL code and examining the result')
const simpleTeal = loadTealSource('simple-approve.teal')
const compiled = await algorand.app.compileTeal(simpleTeal)
printInfo(`\nCompilation result:`) printInfo(` Original TEAL: ${compiled.teal.split('\n').length} lines`) printInfo(` Compiled (base64): ${compiled.compiled.slice(0, 30)}...`) printInfo(` Compiled hash: ${compiled.compiledHash}`) printInfo(` Compiled bytes: ${compiled.compiledBase64ToBytes.length} bytes`) printInfo(` Source map available: ${compiled.sourceMap !== undefined}`)
// Compile the approval program const approvalCompiled = await algorand.app.compileTeal(APPROVAL_PROGRAM) printInfo(`\nApproval program compilation:`) printInfo(` Original: ${APPROVAL_PROGRAM.split('\n').length} lines`) printInfo(` Compiled: ${approvalCompiled.compiledBase64ToBytes.length} bytes`)
printSuccess('TEAL compilation successful')
// Step 12: Demonstrate algorand.app.compileTealTemplate() printStep(12, 'Demonstrate algorand.app.compileTealTemplate() with template variables') printInfo('Compiling TEAL templates with parameter substitution')
// Compile template with custom parameters const compiledTemplate = await algorand.app.compileTealTemplate(TEAL_TEMPLATE, { TMPL_INT_VALUE: 42, TMPL_BYTES_VALUE: 'hello', })
printInfo(`\nTemplate compilation with custom parameters:`) printInfo(` TMPL_INT_VALUE: 42`) printInfo(` TMPL_BYTES_VALUE: "hello"`) printInfo(` Compiled bytes: ${compiledTemplate.compiledBase64ToBytes.length} bytes`)
// Compile AlgoKit template with deploy-time control parameters const compiledUpdatable = await algorand.app.compileTealTemplate( ALGOKIT_TEMPLATE, undefined, { updatable: true, deletable: false }, )
printInfo(`\nAlgoKit template with deploy-time controls:`) printInfo(` updatable: true`) printInfo(` deletable: false`) printInfo(` Compiled bytes: ${compiledUpdatable.compiledBase64ToBytes.length} bytes`)
// Compile with different control values const compiledImmutable = await algorand.app.compileTealTemplate( ALGOKIT_TEMPLATE, undefined, { updatable: false, deletable: false }, )
printInfo(`\nImmutable version (updatable: false, deletable: false):`) printInfo(` Compiled bytes: ${compiledImmutable.compiledBase64ToBytes.length} bytes`) printInfo(` Note: Different control values produce different bytecode`)
printSuccess('TEAL template compilation successful')
// Step 13: Summary printStep(13, 'Summary - App Manager API') printInfo('The AppManager provides comprehensive application query and compile capabilities:') printInfo('') printInfo('algorand.app.getById(appId):') printInfo(' - Fetches complete application information') printInfo(' - Returns: AppInformation with id, address, creator, programs, schemas') printInfo('') printInfo('algorand.app.getGlobalState(appId):') printInfo(' - Reads all global state key-value pairs') printInfo(' - Returns: AppState object keyed by UTF-8 strings') printInfo(' - Values include both raw bytes and decoded forms') printInfo('') printInfo('algorand.app.getLocalState(appId, address):') printInfo(' - Reads an account\'s local state for an app') printInfo(' - Account must be opted in to the application') printInfo(' - Returns: AppState object with local key-value pairs') printInfo('') printInfo('algorand.app.getBoxNames(appId):') printInfo(' - Lists all box names for an application') printInfo(' - Returns: BoxName[] with name, nameRaw, nameBase64') printInfo('') printInfo('algorand.app.getBoxValue(appId, boxName):') printInfo(' - Reads a single box value by name') printInfo(' - Returns: Uint8Array of raw box contents') printInfo('') printInfo('algorand.app.getBoxValues(appId, boxNames):') printInfo(' - Reads multiple box values in one call') printInfo(' - Returns: Uint8Array[] in same order as input names') printInfo('') printInfo('algorand.app.getBoxValuesFromABIType({ appId, boxNames, type }):') printInfo(' - Reads and decodes box values using ABI types') printInfo(' - Supports all ABI types: uint64, string, address, arrays, tuples') printInfo(' - Returns: ABIValue[] decoded according to specified type') printInfo('') printInfo('algorand.app.compileTeal(tealCode):') printInfo(' - Compiles TEAL source code') printInfo(' - Returns: CompiledTeal with compiled bytes, hash, source map') printInfo(' - Results are cached to avoid recompilation') printInfo('') printInfo('algorand.app.compileTealTemplate(template, params?, deployMetadata?):') printInfo(' - Compiles TEAL with template parameter substitution') printInfo(' - Supports custom TMPL_* parameters') printInfo(' - Supports AlgoKit deploy-time controls (TMPL_UPDATABLE, TMPL_DELETABLE)')
// Clean up - close out user and delete app await algorand.send.appCall({ sender: user.addr, appId: appId, onComplete: OnApplicationComplete.CloseOut, }) await algorand.send.appDelete({ sender: creator.addr, appId: appId, })
printSuccess('App Manager example completed!')}
main().catch((error) => { printError(`Unhandled error: ${error instanceof Error ? error.message : String(error)}`) process.exit(1)})Other examples in Algorand Client
Section titled “Other examples in Algorand Client”- Client Instantiation
- AlgoAmount Utility
- Signer Configuration
- Suggested Params Configuration
- Account Manager
- Send Payment
- Send Asset Operations
- Send Application Operations
- Create Transaction (Unsigned Transactions)
- Transaction Composer (Atomic Transaction Groups)
- Asset Manager
- App Manager
- App Deployer
- Client Manager
- Error Transformers
- Transaction Leases