Skip to content

Simple Voting

← Back to Examples

This example demonstrates a minimal voting contract with paid votes.

  • Store topic and total votes in global state
  • Prevent double-voting via local state
  • Validate grouped payment + app call flow
  • Expose methods to set topic, cast vote, and read vote count
  • AlgoKit TypeScript testing setup
  • Familiarity with BaseContract approval/clear state programs

From the repository’s examples directory:

cd examples
npx tsx simple-voting/contract.algo.ts

View source on GitHub

/**
 * Example: Simple Voting
 *
 * This example demonstrates a minimal voting contract with paid votes.
 * - Store topic and total votes in global state
 * - Prevent double-voting via local state
 * - Validate grouped payment + app call flow
 * - Expose methods to set topic, cast vote, and read vote count
 *
 * Prerequisites:
 * - AlgoKit TypeScript testing setup
 * - Familiarity with BaseContract approval/clear state programs
 */
import type { Account, bytes, uint64 } from '@algorandfoundation/algorand-typescript'
import {
  assert,
  BaseContract,
  Bytes,
  GlobalState,
  gtxn,
  LocalState,
  op,
  TransactionType,
  Uint64,
} from '@algorandfoundation/algorand-typescript'

const VOTE_PRICE = Uint64(10_000)
export default class SimpleVotingContract extends BaseContract {
  topic = GlobalState({ initialValue: Bytes('default_topic'), key: Bytes('topic') })
  votes = GlobalState({ initialValue: Uint64(0), key: Bytes('votes') })
  voted = LocalState<uint64>()

  public approvalProgram(): uint64 {
    switch (op.Txn.applicationArgs(0)) {
      case Bytes('set_topic'): {
        this.setTopic(op.Txn.applicationArgs(1))
        return 1
      }
      case Bytes('vote'): {
        return this.vote(op.Txn.sender) ? 1 : 0
      }
      case Bytes('get_votes'): {
        return this.votes.value
      }
      default:
        return 0
    }
  }

  public clearStateProgram(): boolean {
    return true
  }

  private setTopic(topic: bytes): void {
    this.topic.value = topic
  }

  private vote(voter: Account): boolean {
    assert(op.Global.groupSize === 2)
    assert(gtxn.PaymentTxn(1).amount === VOTE_PRICE)
    assert(op.GTxn.amount(1) === VOTE_PRICE)
    assert(op.GTxn.typeEnum(1) === TransactionType.Payment)

    if (this.voted(voter).hasValue) {
      return false
    }
    this.votes.value = this.votes.value + 1
    this.voted(voter).value = Uint64(1)
    return true
  }
}