Program Structure | Algorand Developer Portal

Program Structure

An Algorand TypeScript program is declared in a TypeScript module with a file extension of .algo.ts. Declarations can be split across multiple files, and types can be imported between these files using standard TypeScript import statements. The CommonJS require function is not supported, and the asynchronous import(...) expression is also not supported, as imports must be compile-time constant.

Algorand TypeScript constructs and types can be imported from the @algorandfoundation/algorand-typescript module, or one of its submodules. Compilation artifacts do not need to be exported unless you require them in another module; any non-abstract contract or logic signature discovered in your entry files will be output. Contracts and logic signatures discovered in non-entry files will not be output.

Constants

Constants declared at the module level must have a compile-time constant value or a template variable. Some basic expressions are supported so long as they result in a compile-time constant.

import { uint64 } from '@algorandfoundation/algorand-typescript';

const a: uint64 = 1000;

const b: uint64 = 2000;

const c: uint64 = a * b;

Free Subroutines

Free subroutines can be declared at the module level and called from any contract, logic signature, or other subroutine. Subroutines do not have any compiler output on their own unless they are called by a contract or logic signature.

import { uint64 } from '@algorandfoundation/algorand-typescript';

function add(a: uint64, b: uint64): uint64 {

return a + b;

}

Contracts

A contract in Algorand TypeScript is defined by declaring a class that extends the Contract or BaseContract types exported by @algorandfoundation/algorand-typescript.

ARC4 Contract

Contracts that extend the Contract type are ARC4-compatible contracts. Any public methods on the class will be exposed as ABI methods, callable from other contracts and off-chain clients. private and protected methods can only be called from within the contract itself or its subclasses. Note that TypeScript methods are public by default if no access modifier is present. A contract is considered valid even if it has no methods, though its utility is questionable.

import { Contract } from '@algorandfoundation/algorand-typescript';

class DoNothingContract extends Contract {}

class HelloWorldContract extends Contract {

sayHello(name: string) {

return `Hello ${name}`;

}

}

Contract Options

The contract decorator allows you to specify additional options and configuration for a contract, such as which AVM version it targets, which scratch slots it makes use of, or the total global and local state which should be reserved for it. It should be placed on your contract class declaration.

import { Contract, contract } from '@algorandfoundation/algorand-typescript';

@contract({

name: 'My Contracts Name',

avmVersion: 11,

scratchSlots: [1, 2, 3],

stateTotals: { globalUints: 4, localUints: 0 },

})
class MyContract extends Contract {}

Application Lifecycle Methods and other method options

There are two approaches to handling application lifecycle events: by implementing a well-known method (convention-based), or by using decorators (decorator-based).

Convention-based

Application lifecycle methods can be handled by a convention of well-known method names. The easiest way to discover these method names is to implement the interface ConventionalRouting from the @algorandfoundation/algorand-typescript/arc4 module.

import type { bytes, uint64 } from '@algorandfoundation/algorand-typescript';
import { Contract, log } from '@algorandfoundation/algorand-typescript';
import type { ConventionalRouting } from '@algorandfoundation/algorand-typescript/arc4';

export class TealScriptConventionsAlgo extends Contract implements ConventionalRouting {

closeOutOfApplication(arg: uint64) {

return arg;

}

createApplication(value: bytes) {

log(value);

}

deleteApplication() {}

optInToApplication() {}

updateApplication() {}

customMethod() {}

}

Decorator-based

The default OnCompletionAction (OCA) for public methods is NoOp. To change this, a method should be decorated with the abimethod or baremethod decorators.

import type { uint64 } from '@algorandfoundation/algorand-typescript';
import { abimethod, baremethod, Contract, Uint64 } from '@algorandfoundation/algorand-typescript';

class AbiDecorators extends Contract {

@abimethod({ allowActions: 'NoOp' })
  public justNoop(): void {}

@abimethod({ onCreate: 'require' })
  public createMethod(): void {}

@abimethod({
    allowActions: ['NoOp', 'OptIn', 'CloseOut', 'DeleteApplication', 'UpdateApplication'],
  })
  public allActions(): void {}

@abimethod({ readonly: true, name: 'overrideReadonlyName' })
  public readonly(): uint64 {
    return 5;
  }

@baremethod()
  public noopBare() {}
}

Constructor logic and implicit create method

If a contract does not define an explicit create method (i.e., onCreate: 'allow' or onCreate: 'require'), then the compiler will attempt to add a bare create method with no implementation. Without this, you would not be able to deploy the contract.

export class MyContract extends Contract {

constructor() {
    super();
    log('This is executed on create only');
  }
}

Custom approval and clear state programs

The default implementation of a clear state program on a contract is to just return true; custom logic can be added by overriding the base implementation.

class Arc4HybridAlgo extends Contract {
  override approvalProgram(): boolean {
    log('before');
    const result = super.approvalProgram();
    log('after');
    return result;
  }

override clearStateProgram(): boolean {
    log('clearing state');
    return true;
  }

someMethod() {
    log('some method');
  }
}

Application State

Application state for a contract can be defined by declaring instance properties on a contract class using the relevant state proxy type. In the case of GlobalState, it is possible to define an initialValue for the field.

import {
  Contract,
  uint64,
  bytes,
  GlobalState,
  LocalState,
  Box,
} from '@algorandfoundation/algorand-typescript';

export class ContractWithState extends Contract {
  globalState = GlobalState<uint64>({ initialValue: 123, key: 'customKey' });
  localState = LocalState<string>();
  boxState = Box<bytes>({ key: 'boxKey' });
}

Logic Signatures

Logic signatures, or smart signatures as they are sometimes referred to, are single program constructs that can be used to sign transactions.

import { assert, LogicSig, Txn, Uint64 } from '@algorandfoundation/algorand-typescript';

export class AlwaysAllow extends LogicSig {
  program() {
    return true;
  }
}

function feeIsZero() {
  assert(Txn.fee === 0, 'Fee must be zero');
}

export class AllowNoFee extends LogicSig {
  program() {
    feeIsZero();
    return Uint64(1);
  }
}