> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SauceScript reference

> SauceScript is the TypeScript subset Sauce programs are written in. Program structure, imports, module bindings, functions, literals, operators, control flow, contract calls, type annotations and builtin namespaces, condensed from the compiler documentation.

SauceScript is the language for writing Sauce programs: a **TypeScript subset** for onchain runtime scripts that run within a single transaction. The compiler parses TypeScript, gates it to the supported subset, lowers it to one intermediate representation, and emits Sauce bytecode for the selected target, EVM or SVM. Anything outside the subset is a compile error rather than a silent difference.

This page condenses the compiler's language and builtins references. Check compiler diagnostics and the [compiler API](/sdk-reference/sauce-compiler/api) when adapting a program.

## Program structure

The entry point is `function main()`. Alongside it, a module's top level may contain `function` declarations (including `export function`), `import` declarations, `let` and `const` module-scope bindings, and `interface` declarations. Any other top-level statement, including `var`, expression statements and classes, is a compile error.

```typescript theme={null}
import ERC20 from "./abi/erc20.abi.json";

function helper(x, y) {
  return x + y;
}

function main() {
  return helper(1, 2);
}
```

**Scripts.** A module that declares no `main` and has at least one top-level statement is a script: its top-level statements are the entry point's body, its top-level `let` and `const` are locals, and it returns nothing.

### Import kinds

| Import | Meaning |
| - | - |
| `import { double } from "./math.ts"` | A sibling SauceScript module. `.js`, `.mjs`, `.ts` and `.mts` are modules. |
| `import ERC20 from "./abi/erc20.abi.json"` | A contract ABI. Any other extension, including bare `.json`, binds an ABI. |
| `import cfg from "./cfg.json" with { type: "json" }` | A JSON document read at compile time as data. `with { type: "json" }` is the only attribute the language reads; any other is an error. |

The entry file's directory is the module root. A library can ship per-target arms as sibling files, `token.evm.ts` and `token.svm.ts`, and `import ... from "./token.ts"` resolves the arm matching the compile target first.

### Module-scope bindings

A module-scope `const` is a compile-time constant substituted at every use; its initializer may be any constant expression over the module's own consts. A module-scope `let` is one 256-bit word of memory shared by every function in the module, re-initialized on every execution; it is not persistent state, so use `evm.sstore` or `svm.sstore` for that, and its initializer must be a literal. Neither may read a runtime value, a function call, or an imported name.

A `const` whose initializer is an array or object of literals is a **lookup table**: `TIERS[1]` or `FEES.high` substitutes the element at compile time, the index must be a literal or a const bound to one, and an out-of-range index is always an error. A JSON data import is a lookup table too, and may nest to any depth. A JSON number is an IEEE double, so write a `uint256` as a string (`"1000000000000000000"`); a string that is an integer written down reads as a number, otherwise as `bytes`.

**Ambient modules** (`ambient` in the compiler options) broadcast a module's exports into every module with no `import` line; a name the module already has wins silently. **Defines** (`defines`) inject compile-time constants into every module and, unlike ambient names, override the module's own `const` of that name. Both are build inputs: keep them with the build script.

## Functions

Functions are declared with `function`. Arrow functions and function expressions are not supported, except the zero-parameter arrow a contract call's `.catch()` takes. Recursion is supported. Parameters must be plain identifiers, with no destructuring and no defaults, and may be any value type. A function that is not `: void` must `return` on every path; a function returning a non-scalar must annotate its return type unless it is `main`.

`main` takes parameters: each is decoded at run time from the program input, as ABI-encoded words by default or in the compact format when compiled with `compactArgs`. On the SVM an `Account` parameter is routed to an attached account slot instead of a decoded word. An `Account` has `.address` on both targets, and on the SVM also `.owner` (the owning program) and `.dataLen` (the account's data length), both read-only and unforgeable; check `.owner` first, then `.dataLen`, before reading a layout such as an SPL mint.

**Route bodies.** The SDK's `Base(() => { ... }, options)` form wraps a body in `main` for you and resolves token and protocol globals such as `USDC.approve(Uniswap.UniversalRouter, amount)` before compiling. Route bodies are parsed as JavaScript, so they take no type annotations and no `main` parameters; pass values through `defines`. Standalone programs compiled directly accept the full annotated language described here.

## Variables and literals

* `let` and `const` only; `var` is a compile error. Variables must be initialized at declaration. Object destructuring is not supported; array destructuring of a tuple result is (`const [amount, gas] = q.quote(x);`).
* Assignment supports `=` and the compound operators `+=`, `-=`, `*=`, `**=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=`. `||=`, `&&=`, `??=` and `>>>=` are not supported. `i++` and `i--` are statements only, never values.
* **Numbers** are 256-bit words, exact at every magnitude up to `2**256 - 1`: `42`, `0xff`, `1000000000000000000`, `1_000_000`. An address is a hex literal. Fractions, exponents, binary, octal and leading-zero literals are compile errors. The `n` suffix is accepted and changes nothing.
* **Booleans**: `true` is the word `1`, `false` is `0`.
* **Strings** compile to their UTF-8 bytes. **Hex byte literals** use the `hex` tagged template, for example ``hex`a9059cbb` `` or `hex` \`\` for empty bytes, with no interpolation.

## Operators

| Group | Operators | Notes |
| - | - | - |
| Arithmetic | `+ - * / % **`, unary `-` | Add, subtract, multiply and exponentiate revert on overflow; divide and modulo revert on a zero divisor. Signed variants and `mulDiv`, `addMod`, `mulMod` are under `Math.*`. |
| Comparison | `> < >= <=`, `==`/`===`, `!=`/`!==` | Unsigned. Signed comparisons are `Math.sGt`, `Math.sLt`, `Math.sGte`, `Math.sLte`. |
| Logical | `&& \|\| !` | Short-circuit as in JavaScript, and yield a `bool` (`1 && 7` is `true`, not `7`). `??` is not supported. |
| Ternary | `cond ? a : b` | Evaluates only the selected arm. Both arms must have the same type. Value position only. |
| Bitwise | `& \| ^ ~ << >>` | `>>` is a logical shift; the arithmetic shift is `Math.sShr`. `>>>` is not supported. |

## Control flow

`if`/`else` (with `else if` chains), `while`, C-style `for`, and `for (const x of xs)` over an array are supported, with unlabeled `break` and `continue`. With optimization on (the default), a condition known at compile time is folded away and the untaken arm is dropped, though it is still type-checked.

```typescript theme={null}
function main(xs: Uint256[]): Uint256 {
  let sum = 0;
  for (const x of xs) {
    sum += x;
  }
  return sum;
}
```

## Contract interop

Register a contract by importing its ABI JSON artifact, a bare entry array or a build-tool object with an `abi` field. The compiler computes selectors, ABI-encodes arguments by position, issues the call, and decodes a single return value to its declared type.

| Binding | Call kind |
| - | - |
| `Contract.at(address)` | `view` and `pure` methods use a static call; others a call |
| `Contract.view(address)` | Every method is a static call |
| `Contract.lib(address)` | Every method is a delegate call |

Struct arguments are object literals. Either name every field after a declared component, in which case order does not matter, or name none of them, in which case the keys are a mnemonic and values are read positionally. Naming some, or writing a different number of fields, is a compile error.

A contract call that reverts normally aborts the program. Append `.catch(() => { ... })` to an external call to handle the failure instead. Two forms exist, both EVM-only:

* **Captured result.** For an ABI method returning one scalar, a handler containing exactly `return CONSTANT;` makes the call an expression: the successful result on success, the constant on failure. The constant must fit the result's ABI width.
* **Statement handler.** `call.catch(() => { ok = 0n; });` discards the result and runs ordinary statements on failure. The handler may read and assign enclosing variables; `break`, `continue` and `return` cannot escape it.

```typescript theme={null}
import IERC20 from "./IERC20.json";

function main(token: Address, owner: Address): Uint256 {
  return IERC20.at(token).balanceOf(owner)
    .catch(() => { return 0n; });
}
```

The example uses [IERC20.json](/cookbook/sauce/abis/IERC20.json) beside the source. Only the external call's failure is caught: malformed return data from a successful call still fails ABI decoding. Handlers are synchronous, take no parameters, and apply to ABI calls and raw `evm.call`, `evm.static` and `evm.delegate` only.

## Type annotations

Annotations are optional but checked, and a recognized annotation is a constraint, not code. The types used across the compiler documentation include `Uint256`, `Address`, `Account`, `bool`, `bigint`, `bytes`, arrays such as `Uint256[]`, tuples, `void`, and record types declared with a top-level `interface`. An annotation outside the compiler's vocabulary fails the build; `type` aliases, enums, `as` and `satisfies` casts, arbitrary TypeScript generics and the non-null `!` are rejected as out-of-subset.

## Builtins

Ordinary arithmetic, comparison, boolean, bitwise and ternary operations use the native operators. Everything else is a builtin in one of six reserved namespaces, which may not be used as variable, parameter or import-alias names.

| Namespace | Covers |
| - | - |
| `ctx.*` | Execution context: `ctx.msgSender()`, `ctx.self()`, `ctx.chainId()`, `ctx.blockNumber()`, `ctx.timestamp()`, `ctx.selfBalance()`, `ctx.balance(addr)`, `ctx.msgData()`. `ctx.msgValue()`, `ctx.gasLeft()`, `ctx.codeSize()`, `ctx.codeHash()`, `ctx.isContract()` and `ctx.isEOA()` are EVM-only and a compile error on the SVM. |
| `crypto.*` | `crypto.keccak256(data)`, `crypto.ecdsaVerify(hash, signature, signer)`, `crypto.findPDA(programId, seeds)`. |
| `Math.*` | Extended and signed arithmetic. |
| `abi.*` | `abi.encode(...values)`, `abi.decode(data, shape)`. |
| `evm.*` / `svm.*` | Target-namespaced storage and low-level calls: keyed 256-bit slots and message calls on the EVM (`evm.sload`, `evm.sstore`, `evm.call`, `evm.static`, `evm.delegate`), account byte ranges and cross-program invocation on the SVM (`svm.sload`, `svm.sstore`, `svm.call`, `svm.remainingAccount`). A program that names a target-specific concept says so explicitly. |
| Values, events, aborts | `.slice`, `.concat`, `.length` and byte conversions on values; `log(data, ...topics)`; `eval(code, ...args)` for nested execution; `revert(data?)`, `require(condition, data?)`, `throw data`. |

`ctx.msgSender()` and `ctx.self()` are a 20-byte address on the EVM and a 32-byte pubkey on the SVM; balances are wei on the EVM and lamports on the SVM. `ctx.msgData()` returns the whole program input payload, `[program | STOP | args]`, not Solidity's `msg.data`.

## Cross-target rules

Programs using only cross-target constructs can compile to either engine. Where a construct has the same meaning on both targets and only the encoding differs, the compiler reconciles it behind one builtin. Where a construct names a target-specific concept, it is exposed as two namespaced builtins (`evm.sload` and `svm.sload`) and the capability pass ensures each compiles only for its engine. The SVM engine omits `CATCH`, the `CREATE*` family, `TLOAD`/`TSTORE`, the ABI opcodes and `DELEGATE`/`STATIC`, so programs using them are EVM-only by construction.

## Next steps

* [Compile a program](/sdk-reference/sauce-compiler/api#complete-example-in-node).
* [Try complete programs](/cookbook/sauce/small-programs).
