### Object Definition Example Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/Supported Types/arrays.md Demonstrates defining objects with different property orders, which result in the same underlying byte representation. ```typescript type MyType = { foo: uint64, bar: uint8 } ... const x: MyType = { foo: 1, bar: 2} const y: MyType = { bar: 2, foo: 1 } ``` -------------------------------- ### Install TEALScript via npm Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/getting_started.md Add TEALScript to an existing project by installing the package from npm. ```bash npm install @algorandfoundation/tealscript ``` -------------------------------- ### Example Project Directory Structure Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0002-init.md This is the typical directory structure generated after initializing a TEALScript project. Familiarize yourself with these directories and files for project organization. ```tree . ├── README.md ├── __test__ │ └── hello_world.test.ts ├── contracts │ ├── artifacts │ │ └── components │ ├── clients │ └── hello_world.algo.ts ├── jest.config.js ├── package-lock.json ├── package.json └── tsconfig.json ``` -------------------------------- ### Starlight Project Commands Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/README.md These commands are used to manage your Starlight project. They cover dependency installation, local development server, building for production, and previewing the build. ```bash npm install ``` ```bash npm run dev ``` ```bash npm run build ``` ```bash npm run preview ``` ```bash npm run astro ... ``` ```bash npm run astro -- --help ``` -------------------------------- ### Array Pass-by-Reference Example Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/Supported Types/arrays.md Demonstrates how arrays are passed by reference in TEALScript and how to use `clone` for pass-by-value behavior. ```typescript const x: uint64[] = [1,2,3]; const y = x; y[0] = 4 log(y) // [4, 2, 3] log(x) // [4, 2, 3] const z = clone(x) z[1] = 5 log(x) // [4, 2, 3] note x has NOT changed log(z) // [4, 5, 3] ``` -------------------------------- ### Logic Signature Address and Program Access in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/multiple_contracts.md Demonstrates how to get the address and program of a Logic Signature from within a TEALScript Contract. ```typescript class TheLsig extends LogicSig { logic(): void { assert(this.txn.fee === 0); } } class TheApp extends Contract { createApplication(): void { assert(TheLsig.address().balance >= 10_000_000); log(TheLsig.program()); } } ``` -------------------------------- ### Create Logic Signatures with `LogicSig` Source: https://context7.com/algorandfoundation/tealscript/llms.txt Logic signatures extend the `LogicSig` class and implement a single `logic()` method, compiling to `.lsig.teal` files. `TemplateVar()` creates compile-time template variables that can be filled at deployment time. This example demonstrates a safe opt-in logic signature. ```typescript import { LogicSig } from '@algorandfoundation/tealscript'; // Simple opt-in logic signature: approves zero-amount asset transfers to self class OptInLsig extends LogicSig { // Template variable filled at deployment time APP_ID = TemplateVar(); logic(): void { // Verify this is a safe opt-in: zero amount, receiver is sender, no rekey or close verifyAssetTransferTxn(this.txn, { assetAmount: 0, assetReceiver: this.txn.sender, fee: 0, // Prevent fee draining rekeyTo: globals.zeroAddress, // Prevent rekey attacks assetCloseTo: globals.zeroAddress, }); // Verify the next transaction in the group is a call to our app const appCall = this.txnGroup[this.txn.groupIndex + 1]; assert(appCall.applicationID === this.APP_ID); assert(appCall.applicationArgs[0] === method('verifyCreator(axfer,asset)void')); } } ``` -------------------------------- ### Control OnComplete Routing with @allow Decorators Source: https://context7.com/algorandfoundation/tealscript/llms.txt Utilize @allow.create, @allow.call, @allow.bareCreate, and @allow.bareCall to specify which AVM OnComplete values are permitted for contract methods. This example shows controlling creation and specific call types. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class ManagedApp extends Contract { admin = GlobalStateKey
(); // Allow creation with NoOp OR with OptIn simultaneously @allow.create('NoOp') @allow.create('OptIn') createApplication(initialAdmin: Address): void { this.admin.value = initialAdmin; } // Allow opt-in via this method @allow.call('OptIn') optInToApp(): void { // custom opt-in logic } // Allow update only by admin @allow.call('UpdateApplication') updateApplication(): void { verifyAppCallTxn(this.txn, { sender: this.admin.value }); } // Allow delete only by admin @allow.call('DeleteApplication') deleteApplication(): void { verifyAppCallTxn(this.txn, { sender: this.admin.value }); sendPayment({ receiver: this.admin.value, closeRemainderTo: this.admin.value, amount: 0 }); } } ``` -------------------------------- ### Calculator Contract with Private Subroutines Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/methods.md This example defines a Calculator contract with private helper methods for addition and subtraction, and a public ABI method `doMath` that orchestrates their use. Private methods are not exposed in the ABI. ```typescript class Calculator extends Contract { /** * Calculates the sum of two numbers * * @param a * @param b * @returns The sum of a and b */ private getSum(a: uint64, b: uint64): uint64 { return a + b; } /** * Calculates the difference between two numbers * * @param a * @param b * @returns The difference between a and b. */ private getDifference(a: uint64, b: uint64): uint64 { return a >= b ? a - b : b - a; } /** * A method that takes two numbers and does either addition or subtraction * * @param a The first number * @param b The second number * @param operation The operation to perform. Can be either 'sum' or 'difference' * * @returns The result of the operation */ doMath(a: uint64, b: uint64, operation: string): uint64 { let result: uint64; if (operation === 'sum') { result = this.getSum(a, b); } else if (operation === 'difference') { result = this.getDifference(a, b); } else throw Error('Invalid operation'); return result; } } ``` -------------------------------- ### TEALScript Calculator Contract Example Source: https://github.com/algorandfoundation/tealscript/blob/dev/README.md Defines a smart contract for basic arithmetic operations using TEALScript. It includes private helper methods for sum and difference, and a public method to perform calculations based on an operation string. Ensure correct type casting for numeric literals. ```typescript import { Contract } from '../../src/lib/index'; class Calculator extends Contract { /** * Calculates the sum of two numbers * * @param a * @param b * @returns The sum of a and b */ private getSum(a: uint64, b: uint64): uint64 { return a + b; } /** * Calculates the difference between two numbers * * @param a * @param b * @returns The difference between a and b. */ private getDifference(a: uint64, b: uint64): uint64 { return a >= b ? a - b : b - a; } /** * A method that takes two numbers and does either addition or subtraction * * @param a The first number * @param b The second number * @param operation The operation to perform. Can be either 'sum' or 'difference' * * @returns The result of the operation */ doMath(a: uint64, b: uint64, operation: string): uint64 { let result: uint64; if (operation === 'sum') { result = this.getSum(a, b); } else if (operation === 'difference') { result = this.getDifference(a, b); } else throw Error('Invalid operation'); return result; } } ``` -------------------------------- ### Handle Maybe Values in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/pyteal.md When an opcode might return a MaybeValue in PyTeal, TEALScript provides separate methods to get the value and check for its existence. Accessing the value directly asserts its existence. For boxes, call `exist` before `get`. Otherwise, use the `has` prefixed method to check for existence. ```typescript class BalanceApp extends Contract { counters = BoxMap(); getBalance(addr: Address): void { // Set balance to 0 if addr balance doesn't exist const balance = addr.hasBalance ? addr.balance : 0 // set counter to 1 if it doesn't already exist const newCounter = this.counters(addr).exists ? this.counters(addr).value + 1 : 1 this.counters(addr).value = newCounter } } ``` -------------------------------- ### Valid Method Definition in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/methods.md This example shows a correctly defined method with explicit types for both arguments and the return value. ```typescript add(a: uint64, b: uint64): uint64 { return a + b } ``` -------------------------------- ### Invalid Method Definition: Missing Argument Type Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/methods.md This example demonstrates an invalid method definition where one of the arguments lacks an explicit type. ```typescript add(a, b: uint64): uint64 { // missing argument type return a + b } ``` -------------------------------- ### Create Astro Project with Starlight Template Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/README.md Use this command to initialize a new Astro project with the Starlight template. This sets up the basic structure for your documentation site. ```bash npm create astro@latest -- --template starlight ``` -------------------------------- ### Initialize TEALScript Project with Algokit Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/getting_started.md Use the Algokit CLI to create a new project pre-configured with TEALScript, ESLint, and testing utilities. ```bash algokit init -t tealscript ``` -------------------------------- ### Initialize TEALScript Template with Algokit Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0002-init.md Use this command to create a new TEALScript project from a template. Specify the template and a project name. ```bash $ algokit init --template tealscript --name hello_world ``` -------------------------------- ### Implement Stateful Smart Contracts with `Contract` Source: https://context7.com/algorandfoundation/tealscript/llms.txt Stateful Algorand smart contracts extend the `Contract` class. Public methods become ABI-routed entry points. Lifecycle hooks like `createApplication` and `deleteApplication` can be overridden for custom logic. The `programVersion` property sets the `#pragma version` directive. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class Calculator extends Contract { // Override program version if needed programVersion = 10; // Private helpers are subroutines; not exposed as ABI methods private getSum(a: uint64, b: uint64): uint64 { return a + b; } private getDifference(a: uint64, b: uint64): uint64 { return a >= b ? a - b : b - a; } // Public methods become ABI endpoints doMath(a: uint64, b: uint64, operation: string): uint64 { let result: uint64; if (operation === 'sum') { result = this.getSum(a, b); } else if (operation === 'difference') { result = this.getDifference(a, b); } else throw Error('Invalid operation'); return result; } // Override create to accept arguments createApplication(initialValue: uint64): void { // custom init logic } // Override delete to allow it (default throws) deleteApplication(): void { sendPayment({ receiver: globals.creatorAddress, closeRemainderTo: globals.creatorAddress, amount: 0 }); } } ``` -------------------------------- ### Running Tests Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0006-test.md Execute your tests using the command 'npm run test'. This command will run all defined tests and report their status, indicating success or failure. ```bash PASS __test__/hello_world.test.ts HelloWorld ✓ helloWorld (766 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total ``` -------------------------------- ### Utilize Reference Types: Address, AssetID, AppID Source: https://context7.com/algorandfoundation/tealscript/llms.txt Shows how to use typed references like `Address`, `AssetID`, and `AppID` for interacting with on-chain objects and ABI types. Includes checking balances, reading remote state, and asset creation. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class ReferenceDemo extends Contract { // Check token balances and asset properties checkBalance(user: Address, token: AssetID): uint64 { assert(user.isOptedInToAsset(token)); return user.assetBalance(token); } // Read from another app's global state readRemoteState(otherApp: AppID, key: string): uint64 { assert(otherApp.globalStateExists(key)); return otherApp.globalState(key) as uint64; } // Asset creation, using AssetID properties mintToken(name: string, total: uint64): AssetID { const newAsset = sendAssetCreation({ configAssetName: name, configAssetUnitName: 'TKN', configAssetTotal: total, configAssetDecimals: 6, configAssetManager: this.app.address, configAssetReserve: this.app.address, }); // Access the newly created asset's properties assert(newAsset.total === total); assert(newAsset.manager === this.app.address); return newAsset; } // Use Address static helpers isZeroAddress(addr: Address): boolean { return addr === Address.zeroAddress; } } ``` -------------------------------- ### Starlight Project Structure Overview Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/README.md This is the typical directory structure for an Astro + Starlight project. Key directories include `public/` for static assets and `src/content/docs/` for Markdown/MDX documentation files. ```bash . ├── public/ ├── src/ │ ├── assets/ │ ├── content/ │ │ ├── docs/ │ │ └── config.ts │ └── env.d.ts ├── astro.config.mjs ├── package.json └── tsconfig.json ``` -------------------------------- ### Importing Contracts in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/multiple_contracts.md Shows how to import a base contract from one file into another to enable inheritance. ```typescript // base_contract.algo.ts export class BaseContract extends Contract { favoriteNumber = GlobalStateKey() private setNumber(n: uint64): void { this.favoriteNumber.value = n } } ``` ```typescript // another_contract.algo.ts import { BaseContract } from './base_contract.algo.ts' export class AnotherContract extends BaseContract { checkNumber(): void { this.setNumber(42) assert(this.favoriteNumber.value === 42) } } ``` -------------------------------- ### Local State Management with LocalStateKey and LocalStateMap Source: https://context7.com/algorandfoundation/tealscript/llms.txt Utilize LocalStateKey and LocalStateMap for per-account state storage. Access is done by passing the account address as the first argument. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class AuctionContract extends Contract { previousBidder = GlobalStateKey
(); previousBid = GlobalStateKey(); auctionEnd = GlobalStateKey(); // Local state: each opted-in account stores their claimable amount claimableAmount = LocalStateKey(); optInToApplication(): void {} // Allow opt-in; empty body = success bid(payment: PayTxn): void { assert(globals.latestTimestamp < this.auctionEnd.value); verifyPayTxn(payment, { sender: this.txn.sender, amount: { greaterThan: this.previousBid.value }, }); this.previousBid.value = payment.amount; this.previousBidder.value = payment.sender; // Write into the sender's local state slot this.claimableAmount(this.txn.sender).value = payment.amount; } claimBids(): void { const originalAmount = this.claimableAmount(this.txn.sender).value; let amount = originalAmount; if (this.txn.sender === this.previousBidder.value) { amount = amount - this.previousBid.value; } sendPayment({ receiver: this.txn.sender, amount: amount }); this.claimableAmount(this.txn.sender).value = originalAmount - amount; } } ``` -------------------------------- ### Define a 'Hello World' Method in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0005-hello.md Use this snippet to define a contract with a method that returns a personalized greeting. Ensure the necessary import from '@algorandfoundation/tealscript' is included. ```typescript import { Contract } from "@algorandfoundation/tealscript"; // eslint-disable-next-line no-unused-vars class HelloWorld extends Contract { /** * @param name The name of the caller * * @returns A greeting string */ hello(name: string): string { return "Hello, " + name; } } ``` -------------------------------- ### Accessing App Programs and Schema in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/multiple_contracts.md Illustrates how to access the approval program, clear state program, and schema of another TEALScript app for deployment or interaction. ```typescript class TheApp extends Contract { favoriteNumber = GlobalStateKey(); createApplication(): void { this.favoriteNumber.value = 42; } } class TheFactory extends Contract { createTheApp(): void { sendMethodCall<[], void>({ name: 'createApplication', approvalProgram: TheApp.approvalProgram(), clearStateProgram: TheApp.clearProgram(), globalNumUint: TheApp.schema.global.numUint, }); } } ``` -------------------------------- ### Define a Basic TEALScript Contract Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/getting_started.md Create a smart contract by extending the 'Contract' class and defining methods with TypeScript syntax. Ensure necessary imports are included. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class FirstContract extends Contract { add(a: uint64, b: uint64): uint64 { return a + b } } ``` -------------------------------- ### Access Chain Context with globals and blocks Source: https://context7.com/algorandfoundation/tealscript/llms.txt Demonstrates accessing global chain information and recent block data using `globals` and `blocks`. Useful for time-based logic and network status checks. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class TimeLocked extends Contract { unlockRound = GlobalStateKey(); creator = GlobalStateKey
(); createApplication(lockDuration: uint64): void { this.creator.value = this.txn.sender; this.unlockRound.value = globals.round + lockDuration; } withdraw(): void { // Only creator, only after unlock round assert(this.txn.sender === this.creator.value); assert(globals.round >= this.unlockRound.value); // Use block timestamp for logging const ts = blocks[0].timestamp; log(itob(ts)); sendPayment({ receiver: this.creator.value, closeRemainderTo: this.creator.value, amount: 0, }); } getNetworkInfo(): uint64 { // Access chain globals assert(globals.opcodeBudget > 100); return globals.latestTimestamp; } } ``` -------------------------------- ### Global State Management with GlobalStateKey and GlobalStateMap Source: https://context7.com/algorandfoundation/tealscript/llms.txt Use GlobalStateKey for single typed keys and GlobalStateMap for maps with a fixed maximum number of keys in global state. Access state via .value, .exists, and .delete(). ```typescript import { Contract } from '@algorandfoundation/tealscript'; class TokenVault extends Contract { // Single global state slot with explicit key name admin = GlobalStateKey
({ key: 'admin' }); totalDeposited = GlobalStateKey({ key: 'total' }); // Map up to 10 user balances in global state (key is prefixed with 'bal') balances = GlobalStateMap({ maxKeys: 10, prefix: 'bal' }); createApplication(): void { this.admin.value = this.txn.sender; this.totalDeposited.value = 0; } deposit(payment: PayTxn): void { verifyPayTxn(payment, { receiver: this.app.address }); const current = this.balances(this.txn.sender).exists ? this.balances(this.txn.sender).value : (0 as uint64); this.balances(this.txn.sender).value = current + payment.amount; this.totalDeposited.value = this.totalDeposited.value + payment.amount; } getBalance(user: Address): uint64 { return this.balances(user).exists ? this.balances(user).value : (0 as uint64); } deleteAccount(): void { this.balances(this.txn.sender).delete(); } } ``` -------------------------------- ### Static Array forEach Iteration Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/Supported Types/arrays.md Illustrates efficient iteration over a static array using the `.forEach` method. Note that the index argument and non-arrow functions are not currently supported. ```typescript staticForEach(): uint64 { const a: StaticArray = [1, 2, 3]; let sum = 0; a.forEach((v) => { sum += v; }); return sum; // 6 } ``` -------------------------------- ### Contract Inheritance in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/multiple_contracts.md Demonstrates how to inherit properties and methods from a base contract to a derived contract. ```typescript export class BaseContract extends Contract { favoriteNumber = GlobalStateKey() private setNumber(n: uint64): void { this.favoriteNumber.value = n } } export class AnotherContract extends BaseContract { checkNumber(): void { this.setNumber(42) assert(this.favoriteNumber.value === 42) } } ``` -------------------------------- ### Compile TEALScript Contract Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/getting_started.md Compile your TypeScript smart contract files into TEAL artifacts using the TEALScript CLI. Specify an output directory if needed. ```bash npx tealscript [artifact directory] ``` -------------------------------- ### Test Contract Call Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0006-test.md Modify your test file to call the 'hello' method of your application client and assert the expected return value. This replaces previous tests for 'sum' and 'difference'. ```typescript test("helloWorld", async () => { const message = await appClient.hello({ name: "AlgoDev" }); expect(message.return?.valueOf()).toBe("Hello, AlgoDev"); }); ``` -------------------------------- ### Implement Large Box Storage with Metadata Source: https://context7.com/algorandfoundation/tealscript/llms.txt This pattern handles data larger than 32KB by splitting it across multiple boxes indexed by uint64. A metadata box tracks the upload state, including start/end box indices, status, and end box size. ```typescript import { Contract } from '@algorandfoundation/tealscript'; type Metadata = { start: uint64; end: uint64; status: uint<8>; endSize: uint64 }; const IN_PROGRESS = 0; const READY = 1; const IMMUTABLE = 2; const MAX_BOX_SIZE = 32768; const COST_PER_BYTE = 400; const COST_PER_BOX = 2500; class BigBox extends Contract { dataBoxes = BoxMap(); metadata = BoxMap(); currentIndex = GlobalStateKey(); startUpload(dataIdentifier: string, numBoxes: uint64, endBoxSize: uint64, mbrPayment: PayTxn): void { const startBox = this.currentIndex.value; const endBox = startBox + numBoxes - 1; assert(!this.metadata(dataIdentifier).exists); this.metadata(dataIdentifier).value = { start: startBox, end: endBox, status: IN_PROGRESS, endSize: endBoxSize }; this.currentIndex.value = endBox + 1; const totalCost = numBoxes * COST_PER_BOX + (numBoxes - 1) * MAX_BOX_SIZE * COST_PER_BYTE + numBoxes * 64 * COST_PER_BYTE + endBoxSize * COST_PER_BYTE; verifyPayTxn(mbrPayment, { receiver: this.app.address, amount: totalCost }); } upload(dataIdentifier: string, boxIndex: uint64, offset: uint64, data: bytes): void { const m = this.metadata(dataIdentifier).value; assert(m.status === IN_PROGRESS); assert(m.start <= boxIndex && boxIndex <= m.end); if (offset === 0) { this.dataBoxes(boxIndex).create(boxIndex === m.end ? m.endSize : MAX_BOX_SIZE); } this.dataBoxes(boxIndex).replace(offset, data); } setStatus(dataIdentifier: string, status: uint<8>): void { assert(this.metadata(dataIdentifier).value.status !== IMMUTABLE); this.metadata(dataIdentifier).value.status = status; } } ``` -------------------------------- ### Box Storage with BoxKey and BoxMap Source: https://context7.com/algorandfoundation/tealscript/llms.txt Use BoxKey for single named boxes and BoxMap for key-value mappings where each key is stored in a separate box. Boxes support various operations including .value, .exists, .delete(), .create(), .replace(), .extract(), .splice(), and .resize(). ```typescript import { Contract } from '@algorandfoundation/tealscript'; type Contact = { name: string; company: string }; class ContactsApp extends Contract { // BoxMap: key = Address, value = Contact struct contacts = BoxMap(); // GlobalStateKey for own contact (stored in global state) myContact = GlobalStateKey(); setMyContact(name: string, company: string): void { const contact: Contact = { name: name, company: company }; this.myContact.value = contact; this.contacts(this.txn.sender).value = contact; } addContact(name: string, company: string, address: Address): void { this.contacts(address).value = { name: name, company: company }; } // Update a single field inside a struct stored in a box updateContactField(field: string, value: string, address: Address): void { if (field === 'name') { this.contacts(address).value.name = value; } else if (field === 'company') { this.contacts(address).value.company = value; } else throw Error('Invalid field'); } verifyContactName(name: string, address: Address): void { assert(this.contacts(address).value.name === name); } } ``` -------------------------------- ### Compile TEALScript Source Files with CLI Source: https://context7.com/algorandfoundation/tealscript/llms.txt Use the `tealscript` CLI to compile `.algo.ts` files into TEAL artifacts. Specify the output directory as the last argument. Options include skipping algod validation, disabling warnings, and disabling overflow checks. ```bash npx tealscript myContract.algo.ts ./artifacts ``` ```bash npx tealscript "contracts/*.algo.ts" ./artifacts ``` ```bash npx tealscript --skip-algod myContract.algo.ts ./artifacts ``` ```bash npx tealscript --disable-warnings myContract.algo.ts ./artifacts ``` ```bash npx tealscript --unsafe-disable-overflow-checks myContract.algo.ts ./artifacts ``` ```bash npx tealscript --version ``` ```bash ALGOD_SERVER=http://localhost ALGOD_PORT=4001 ALGOD_TOKEN=aaaa... \ npx tealscript myContract.algo.ts ./artifacts ``` -------------------------------- ### Static Array Partial Definition Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/Supported Types/arrays.md Shows how to partially define a static array, where uninitialized elements default to zero bytes. ```typescript const x: = [1] const y: = [1, 0, 0] ``` -------------------------------- ### Import Contract Class Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0003-contract.md Imports the essential 'Contract' class from the '@algorandfoundation/tealscript' package. Every TEALScript contract must extend this class. ```typescript import { Contract } from '@algorandfoundation/tealscript'; ``` -------------------------------- ### Declare Global State Map with Max Keys Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/storage.md Use `GlobalStateMap` with the required `maxKeys` parameter to reserve a specific number of keys for the map in global storage. ```typescript // Reserve 3 keys for this map class MyApp extends Contract { data = GlobalStateMap({ maxKeys: 3 }) createApplication(){ this.data('name').value = 'MyApp' this.data('version').value = '1.0.0' this.data('author').value = 'Me' } } ``` -------------------------------- ### Declare Box Storage Maps with Prefixes Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/storage.md Use the `prefix` option in `BoxMap` to differentiate between maps with the same key and value types, preventing key collisions. ```typescript class MyApp extends Contract { favoriteColor = BoxMap({ prefix: 'c' }) favoriteNumber = BoxMap({ prefix: 'n' }) setColor(color: string): void { this.favoriteColor(this.txn.sender).value = color // on chain: ("c" + sender's address) now points to their favorite color } setNumber(number: uint64): void { this.favoriteNumber(this.txn.sender).value = number // on chain: ("n" + sender's address) now points to their favorite number } } ``` -------------------------------- ### TEALScript Project Structure Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0004-artifacts.md This is the directory structure generated after compiling a TEALScript project. It shows the location of key artifacts like ABI JSON, TEAL files, and the auto-generated client. ```tree contracts ├── artifacts │   ├── HelloWorld.abi.json │   ├── HelloWorld.approval.teal │   ├── HelloWorld.clear.teal │   ├── HelloWorld.json │   ├── HelloWorld.src_map.json │   └── components ├── clients │   └── HelloWorldClient.ts └── hello_world.algo.ts ``` -------------------------------- ### Programmatically Compile TEALScript Contracts Source: https://context7.com/algorandfoundation/tealscript/llms.txt Use the Compiler class to compile TEALScript contracts from Node.js or Bun. Ensure tsconfig.json is present and environment variables for Algorand node connection are set if not using defaults. ```typescript import { Compiler, Project } from '@algorandfoundation/tealscript'; import { Project as TsProject } from 'ts-morph'; import * as path from 'path'; import * as fs from 'fs'; async function compileContract(srcPath: string, outputDir: string) { const project = new TsProject({ tsConfigFilePath: path.join(process.cwd(), 'tsconfig.json'), }); const compilers = Compiler.compileAll({ srcPath, project, cwd: process.cwd(), algodServer: process.env.ALGOD_SERVER ?? 'http://localhost', algodToken: process.env.ALGOD_TOKEN ?? 'a'.repeat(64), algodPort: Number(process.env.ALGOD_PORT ?? 4001), disableWarnings: false, skipAlgod: false, }); for (const compilerPromise of compilers) { const compiler = await compilerPromise; const { name } = compiler; // Write approval and clear programs fs.writeFileSync(path.join(outputDir, `${name}.approval.teal`), compiler.teal.approval.map(t => t.teal).join('\n')); fs.writeFileSync(path.join(outputDir, `${name}.clear.teal`), compiler.teal.clear.map(t => t.teal).join('\n')); // Write ABI/application specs fs.writeFileSync(path.join(outputDir, `${name}.arc4.json`), JSON.stringify(compiler.arc4Description(), null, 2)); fs.writeFileSync(path.join(outputDir, `${name}.arc32.json`), JSON.stringify(compiler.arc32Description(), null, 2)); fs.writeFileSync(path.join(outputDir, `${name}.arc56.json`), JSON.stringify(compiler.arc56Description(), null, 2)); // Write source map fs.writeFileSync(path.join(outputDir, `${name}.src_map.json`), JSON.stringify(compiler.sourceInfo, null, 2)); console.log(`Compiled ${name}: approval=${compiler.teal.approval.length} lines`); } } compileContract('./contracts/MyContract.algo.ts', './artifacts'); ``` -------------------------------- ### Override createApplication Logic in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/lifecycle.md Implement `createApplication` to define custom logic when an application is created, such as setting initial global state values. ```typescript class Counter extends Contract { counter = GlobalStateKey(); createApplication(startingNumber: uint64): void { this.counter.value = startingNumber } } ``` -------------------------------- ### Implement deleteApplication in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/lifecycle.md Override `deleteApplication` to allow contract deletion. Verify that the sender is the application creator. ```typescript class Counter extends Contract { counter = GlobalStateKey(); createApplication(startingNumber: uint64): void { this.counter.value = startingNumber } deleteApplication(): void { assert(this.txn.sender === this.app.creator) } } ``` -------------------------------- ### Implement updateApplication in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/lifecycle.md Override `updateApplication` to allow contract updates. Ensure the sender is the application creator before proceeding. ```typescript class Counter extends Contract { counter = GlobalStateKey(); createApplication(startingNumber: uint64): void { this.counter.value = startingNumber } updateApplication(): void { assert(this.txn.sender === this.app.creator) } } ``` -------------------------------- ### Verify Merkle Proof with SHA256 Source: https://context7.com/algorandfoundation/tealscript/llms.txt Implements Merkle proof verification using SHA256 hashing. Requires a root hash and checks against provided data and sibling hashes. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class MerkleVerifier extends Contract { root = GlobalStateKey(); private hashPair(left: bytes32, right: bytes32): bytes32 { return sha256(left + right); } verify(data: bytes, sibling: bytes32, isRight: boolean): boolean { const leaf = sha256(data); const computed = isRight ? this.hashPair(leaf, sibling) : this.hashPair(sibling, leaf); return computed === this.root.value; } checkKeccak(data: bytes, expected: bytes32): void { assert(keccak256(data) === expected); } ecdsaExample( data: bytes32, r: bytes32, s: bytes32, x: bytes32, y: bytes32 ): boolean { return ecdsaVerify('Secp256k1', data, r, s, x, y); } // 128-bit arithmetic safeMul(a: uint64, b: uint64): SplitUint128 { return mulw(a, b); // returns { low: uint64, high: uint64 } } // Wide ratio: computes (a * scale) / b without overflow ratio(a: uint64, b: uint64, scale: uint64): uint64 { return wideRatio([a, scale], [b]); } } ``` -------------------------------- ### Sending Inner Transactions with `send*` Functions Source: https://context7.com/algorandfoundation/tealscript/llms.txt Use `send*` functions to issue inner transactions. `sendMethodCall` is for typed ABI method calls and returns the method's result. Ensure necessary imports for `Contract`, `AssetID`, and `Address`. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class NFTFactory extends Contract { createNFT(name: string, unitName: string): AssetID { return sendAssetCreation({ configAssetName: name, configAssetUnitName: unitName, configAssetTotal: 1, }); } transferNFT(asset: AssetID, receiver: Address): void { sendAssetTransfer({ assetReceiver: receiver, assetAmount: 1, xferAsset: asset }); } } class FactoryCaller extends Contract { mintAndGetAsset(): AssetID { // Deploy a new NFTFactory app via inner transaction sendMethodCall({ clearStateProgram: NFTFactory.clearProgram(), approvalProgram: NFTFactory.approvalProgram(), }); const factoryApp = this.itxn.createdApplicationID; // Fund the newly created app sendPayment({ amount: 200_000, receiver: factoryApp.address }); // Typed method call: return value is AssetID const createdAsset = sendMethodCall({ applicationID: factoryApp, methodArgs: ['My NFT', 'MNFT'], }); // Opt in and transfer the NFT sendAssetTransfer({ assetReceiver: this.app.address, assetAmount: 0, xferAsset: createdAsset }); sendMethodCall({ applicationID: factoryApp, methodArgs: [createdAsset, this.app.address], }); return createdAsset; } } ``` -------------------------------- ### Declare Global State Key with Custom On-Chain Key Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/storage.md Use the `key` option in `GlobalStateKey` to specify a custom on-chain key name, useful for shorter or more descriptive keys. ```typescript class MyApp extends Contract { someKey = GlobalStateKey({ key: 'sk' }) setSomeKey(value: string): void { this.someKey.value = value // On chain: "sk" now contains value } } ``` -------------------------------- ### Control OnComplete Calls with Decorators in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/lifecycle.md Use `@allow.create` and `@allow.call` decorators to specify which OnComplete types a method can be invoked with, particularly useful for managing local state. ```typescript class Counter extends Contract { counter = LocalStateKey(); // This method will increment a counter in local state @allow.create('OptIn') // Allow an OptIn create so the creators counter can be set when creating the app @allow.call('OptIn') // Allow anyone to OptIn to the contract so they can use local state @allow.call('NoOp') // Allow anyone to call the app again with a NoOp call (can only OptIn once) useLocalState(): void { if (!this.counter(this.txn.sender).exists) this.counter(this.txn.sender).value = 1 else this.counter(this.txn.sender).value = this.counter(this.txn.sender).value + 1 } } ``` -------------------------------- ### Verify Payment Transaction in TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/atomic_txn.md Use `verifyPayTxn` to check the receiver and amount of an incoming payment transaction. Ensure the amount is greater than a specified threshold. ```typescript fundContractAccount(payment: PayTxn): void { verifyPayTxn({ receiver: this.app.address, amount: { greaterThan: 100_000 } }) } ``` -------------------------------- ### Declare Global State Key Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/storage.md Use `GlobalStateKey` to declare a single key in global storage. The property name is used as the on-chain key by default. ```typescript class MyApp extends Contract { someKey = GlobalStateKey() setSomeKey(value: string): void { this.someKey.value = value // On chain: "someKey" now contains value } } ``` -------------------------------- ### Perform 128-bit Arithmetic with wideRatio and mulw Source: https://context7.com/algorandfoundation/tealscript/llms.txt Utilizes wide-integer arithmetic functions like `wideRatio` and `mulw` for safe 128-bit calculations, essential for DeFi to prevent overflow. ```typescript import { Contract } from '@algorandfoundation/tealscript'; const SCALE = 1_000; const FEE = 5; class AMMSwap extends Contract { private tokensToSwap(inAmount: uint64, inSupply: uint64, outSupply: uint64): uint64 { const factor = SCALE - FEE; // (inAmount * factor * outSupply) / (inSupply * SCALE + inAmount * factor) return wideRatio( [inAmount, factor, outSupply], [inSupply * SCALE + inAmount * factor] ); } private computeLP(aAmount: uint64, bAmount: uint64): uint64 { // sqrt is natively supported on uint64 return sqrt(aAmount * bAmount); } private minted(issued: uint64, aSupply: uint64, aAmount: uint64): uint64 { // (aAmount * SCALE) / aSupply const ratio = wideRatio([aAmount, SCALE], [aSupply]); // (ratio * issued) / SCALE return wideRatio([ratio, issued], [SCALE]); } } ``` -------------------------------- ### Declare Box Storage Map Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/storage.md Use `BoxMap` to declare a mapping in box storage. It takes key and value types and an optional `prefix` for key namespacing. ```typescript class MyApp extends Contract { favoriteColor = BoxMap() setColor(color: string): void { this.favoriteColor(this.txn.sender).value = color // on chain: sender's address now points to their favorite color } } ``` -------------------------------- ### Define Unsigned 64-bit Integer Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/Supported Types/numbers.md Use uint64 for efficient arithmetic operations. This is the default numeric type in the AVM. ```typescript const n = 1 ``` -------------------------------- ### Configure ESLint for TEALScript Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/guides/getting_started.md Override specific ESLint rules to ensure compatibility with TEALScript development, particularly for files ending in '.algo.ts'. ```javascript overrides: [ { files: ['*.algo.ts'], rules: { 'object-shorthand': 'off', 'class-methods-use-this': 'off', 'no-undef': 'off', 'max-classes-per-file': 'off', 'no-bitwise': 'off', }, } ], ``` -------------------------------- ### Log ARC28 Events with EventLogger Source: https://context7.com/algorandfoundation/tealscript/llms.txt The EventLogger utility allows logging structured ARC28-compatible events to the blockchain. Each call to .log() serializes the provided argument object. ```typescript import { Contract } from '@algorandfoundation/tealscript'; type TransferEvent = { from: Address; to: Address; amount: uint64 }; type ApprovalEvent = { owner: Address; spender: Address; amount: uint64 }; class TokenContract extends Contract { balances = GlobalStateMap({ maxKeys: 100 }); // Declare event loggers as class fields Transfer = new EventLogger(); Approval = new EventLogger(); transfer(to: Address, amount: uint64): void { const from = this.txn.sender; assert(this.balances(from).value >= amount); this.balances(from).value = this.balances(from).value - amount; this.balances(to).value = this.balances(to).value + amount; // Emit ARC28 Transfer event this.Transfer.log({ from: from, to: to, amount: amount }); } } ``` -------------------------------- ### Verifying Transaction Fields with `verify*Txn` Source: https://context7.com/algorandfoundation/tealscript/llms.txt The `verify*Txn` family asserts fields of transactions within the current group. These functions generate efficient TEAL assert opcodes and support comparison operators for flexible validation. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class ConstantProductAMM extends Contract { governor = GlobalStateKey
({ key: 'g' }); assetA = GlobalStateKey({ key: 'a' }); assetB = GlobalStateKey({ key: 'b' }); bootstrap(seed: PayTxn, aAsset: AssetID, bAsset: AssetID): void { // Only the governor can call this verifyAppCallTxn(this.txn, { sender: this.governor.value }); // Exactly two transactions in the group assert(globals.groupSize === 2); // Payment must be at least 300,000 µALGO to this app verifyPayTxn(seed, { receiver: this.app.address, amount: { greaterThanEqualTo: 300_000 }, }); assert(aAsset < bAsset); this.assetA.value = aAsset; this.assetB.value = bAsset; } swap(swapXfer: AssetTransferTxn, aAsset: AssetID, bAsset: AssetID): void { // Asset transfer must be one of the two pool assets verifyAssetTransferTxn(swapXfer, { assetAmount: { greaterThan: 0 }, assetReceiver: this.app.address, sender: this.txn.sender, xferAsset: { includedIn: [aAsset, bAsset] }, }); // ... swap logic } } ``` -------------------------------- ### Private Method: getSum Source: https://github.com/algorandfoundation/tealscript/blob/dev/starlight/src/content/docs/tutorials/Hello World/0003-contract.md A private method to calculate the sum of two uint64 numbers. Private methods can only be called internally by other methods within the same class. ```typescript /** * Calculates the sum of two numbers * * @param a * @param b * @returns The sum of a and b */ private getSum(a: uint64, b: uint64): uint64 { return a + b; } ``` -------------------------------- ### Implement ARC72 Read-Only Methods with @abi.readonly Source: https://context7.com/algorandfoundation/tealscript/llms.txt Use the @abi.readonly decorator to mark methods that can be simulated off-chain without incurring transaction fees. This is useful for querying contract state. ```typescript import { Contract } from '@algorandfoundation/tealscript'; class ARC72 extends Contract { tokenBox = BoxMap; controller: Address }>(); index = GlobalStateKey(); @abi.readonly arc72_ownerOf(tokenId: uint256): Address { return this.tokenBox(tokenId).value.owner; } @abi.readonly arc72_totalSupply(): uint256 { return this.index.value; } @abi.readonly arc72_tokenByIndex(index: uint256): uint256 { return index; } } ```