> ## 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.

# Types

> SauceScript is statically typed, and every value's type is fixed when the program compiles. Use this reference to check what each type holds and accepts.

SauceScript is statically typed. Every value has one of the types below, fixed when the program compiles, and a variable keeps the type of its first value. Using a value of the wrong type is a compile error.

| Type | Values | Spelled also |
| - | - | - |
| `Uint256` | an unsigned 256-bit integer | `number`, `bigint` |
| `Int256` | a signed 256-bit integer, from −2²⁵⁵ to 2²⁵⁵ − 1 | |
| `Address` | an address: a number | |
| `bool` | `true` or `false` | `boolean` |
| `bytes` | a byte string | `string` |
| `T[]` | an array whose elements all have type `T` | |
| `{ a: T, b: U }`, or an `interface` name | a record with named fields | |
| `Account`, `Account<Writable>`, `Account<Signer>`, `Account<Writable, Signer>` | Solana: an account the transaction attaches. EVM: bare `Account` only, an address | |
| `void` | no value; return types only | |

## Integers

SauceScript has two integer types, both 256 bits wide: `Uint256`, unsigned, from 0 to 2²⁵⁶ − 1, and [`Int256`](#int256), signed. An unannotated integer is a `Uint256`. There are no fractions, so an amount is written in the token's smallest unit: USDC has 6 decimals, so 1 USDC is `1000000`, and 1 ETH is `10 ** 18` wei.

```ts integers.sauce.ts theme={null}
function main(amount: Uint256): Uint256 {
  const oneToken = 10 ** 18;
  const mask = 0xff;
  const million = 1_000_000;
  const max = ~0;
  require(amount < max, "at the maximum");
  return (amount * oneToken) / million + mask;
}
```

* Integers are written as decimal (`3000`), hexadecimal (`0xff`) or with `_` separators (`1_000_000`). Each literal is read from its text as a 256-bit integer, so a value above 2⁵³ is exact. TypeScript's bigint suffix `n` compiles to the same integer; write integers without it.
* `~0` is the maximum `Uint256`, 2²⁵⁶ − 1.
* `1e18` and `1.5` are rejected: the compile fails with "`1e18` is not a valid 256-bit literal". Write `10 ** 18`, as in the program above, or the amount in the smallest unit.

### Checked arithmetic

`+`, `-`, `*`, `/`, `%` and `**` are checked. An overflow, an underflow or a division by zero reverts the transaction, with one exception: an array literal indexed at a literal position (or at a `const` bound to one) skips an unread element when no call or token read is at or after it. With no helper call, contract call or token read in the literal, only the element taken is evaluated, so `[1 / zero, 7][1]` returns 7 for `zero` 0. With one, every element up to and including the last call or token read runs, in order: `[1 / zero, pot.USDC][1]` reverts, and `[pot.USDC, 1 / zero][0]` returns the Pot's USDC. A builtin such as `Math.mulDiv` is not a call: `[Math.mulDiv(1, 1, zero), 7][1]` returns 7 ([Arrays](#arrays)). An object literal that has no call, read by field where it is written, as in `({ a: 1 / zero, b: 7 }).b`, behaves the same. When every operand is a constant, the compiler evaluates the expression and refuses it instead: `return 0 - 1;` in a function that returns `Uint256` fails with "constant subtraction underflows Uint256; for a negative result, convert an operand before subtracting with `Int256(value)`, or give the literal expression a `: Int256` binding or return annotation". With an `Int256` return type the same expression is `-1`:

```ts underflow-signed.sauce.ts theme={null}
function main(): Int256 {
  return 0 - 1;
}
```

A run-time `a - b` with `b > a` reverts. On a local fork of Base at block 52965000 with the current contracts, `a = 1` and `b = 2` revert with no reason data. [Errors and reverts](https://docs.eco.com/sauce/errors.md) covers how a revert surfaces.

## Int256

`Int256` holds a signed value from −2²⁵⁵ to 2²⁵⁵ − 1. A value is an `Int256` when it is:

* a negative literal, such as `-10`;
* a contract output that the ABI declares `intN` (`int24`, `int128`, `int256`), such as a pool's `tick`;
* the result of `Math.signExtend(value, byte)`, which reads the low `byte + 1` bytes of `value` as a two's-complement integer: `Math.signExtend(0xfffff6, 2)` is `-10`.

An annotation names the type: `const delta: Int256 = -10;`. A parameter or a return type can be `Int256` too, and `sauce simulate` prints the value signed:

```ts int256.sauce.ts theme={null}
// The tick of the WETH/USDC 0.05% pool, moved by `shift` ticks in either direction.
//
//   sauce simulate int256.sauce.ts --chain base --owner <address> --args <shift>
//
//   shift: Int256   ticks to add: -100 moves the tick down by 100
function main(shift: Int256): Int256 {
  const pool = UniswapV3.Factory.getPool(WETH, USDC, 500);
  const [sqrtPriceX96, tick] = UniswapV3.Pool(pool).slot0();
  return tick + shift;
}
```

On a local fork of Base at block 52965000 with the current contracts, `--args -100` prints `main returns Int256: -198657`; the pool's own tick is −198557.

On `Int256`, the operators are signed: `/` truncates toward zero, `%` takes the sign of the dividend, `>>` copies the sign bit, and `<`, `>`, `<=` and `>=` order negative values below positive ones. [Expressions and control flow](https://docs.eco.com/sauce/expressions-and-control-flow.md#signed-arithmetic) lists each rule with the values it was simulated on. `Int256.fromBits(word)` reinterprets a `Uint256` as signed; the `from-bits.sauce.ts` program below divides one, `Int256.fromBits(word) / 2`. Dividing the minimum, −2²⁵⁵, by `-1` has no result, so it reverts.

### A negative literal is never a Uint256

A negative constant does not fit `Uint256`, and the compiler says so. `const delta: Uint256 = -10;` fails with "constant Int256 value is outside Uint256's range; conversions check bounds, so choose a representable value or a type that includes it". Write `const delta: Int256 = -10;`. A negative value held in an `Int256` that is converted to `Uint256` at run time reverts instead (see below).

### Converting between the kinds

`Int256(x)` and `Uint256(x)` convert a value, and both check it: `Int256(x)` reverts when `x` is above 2²⁵⁵ − 1, and `Uint256(x)` reverts when `x` is negative. The program below adds a signed `delta` to an unsigned `balance`:

```ts conversions.sauce.ts theme={null}
// Adds a signed `delta` to an unsigned `balance`.
//
//   sauce simulate conversions.sauce.ts --chain base --owner <address> --args <balance> <delta>
//
//   balance: Uint256   at most 2**255 - 1
//   delta: Int256      negative to subtract; the sum must not be negative
function main(balance: Uint256, delta: Int256): Uint256 {
  return Uint256(Int256(balance) + delta);
}
```

On a local fork of Base at block 52965000 with the current contracts:

| `balance` | `delta` | Result |
| - | - | - |
| 100 | -30 | returns 70 |
| 100 | -130 | reverts with no data: the sum is negative |
| 2²⁵⁵ − 1 | 0 | returns 2²⁵⁵ − 1 |
| 2²⁵⁵ | 0 | reverts with no data: `Int256(balance)` is out of range |

`Int256.fromBits(word)` and `Uint256.fromBits(value)` keep the 256 bits and change the type, whatever number they then mean. Use them to read a word you hold as a signed value:

```ts from-bits.sauce.ts theme={null}
// Reads `word` as a signed integer and halves it.
//
//   sauce simulate from-bits.sauce.ts --chain base --owner <address> --args <word>
//
//   word: Uint256   a 256-bit word
function main(word: Uint256): Int256 {
  return Int256.fromBits(word) / 2;
}
```

With `word` equal to 2²⁵⁶ − 10, simulated on the same fork, this returns `-5`. Without `fromBits`, `Int256(word)` would revert, because that word is above 2²⁵⁵ − 1.

### Mixed arithmetic

`+`, `-`, `*`, `/`, `%` and `**` need both operands to be the same kind. A `Uint256` and an `Int256` in one expression are refused until one is converted:

| Mistake | Compiler message | Write instead |
| - | - | - |
| `amount + delta`, with `amount: Uint256` and `delta: Int256` | "cannot combine Uint256 and Int256 in this expression; convert an operand with `Int256(value)` or `Uint256(value)` first (conversions check bounds)" | `Int256(amount) + delta`, as in `conversions.sauce.ts` above |

A literal takes the type its context needs, so `delta * 3` is an `Int256` expression. Comparisons are the exception to the rule above. They compare numeric values across the two kinds, and a negative `Int256` is below every `Uint256`: on the same fork, `b < a` is `true` for `b = -1` and `a = 2²⁵⁶ − 1`. To compare a raw word as a signed number, convert it with `Int256.fromBits`: for `word = 2²⁵⁶ − 7`, `word < 2` is `false` and `Int256.fromBits(word) < 2` is `true`.

### Negation

`-x` on an `Int256` negates it, and reverts for the minimum, −2²⁵⁵, whose negation does not fit. `-x` on a `Uint256` compiles but reverts for any nonzero `x`, because the result is negative; `-0` is `0`. To negate an unsigned amount, convert it first:

```ts negate.sauce.ts theme={null}
// The negative of `amount`, as a signed value.
function main(amount: Uint256): Int256 {
  return -Int256(amount);
}
```

On the same fork, `amount = 5` returns `-5`. The program `function main(a: Uint256): Uint256 { return -a; }` reverts for `a = 5` and returns `0` for `a = 0`.

### Reserved names

`Uint256` and `Int256` are reserved. A parameter, a constant or a function with either name is refused, so rename it. `const Int256 = 5;` fails with "`Int256` is a builtin constructor and cannot be bound — constructor calls resolve against the builtin before any binding is consulted, so pick another name".

### Signed values at a contract boundary

A contract call converts each argument to the ABI type of its parameter and checks it against that type's width, and does the same to each integer it reads back. An `intN` parameter takes an `Int256`, a `uintN` parameter a `Uint256`.

**Narrow integers are checked.** `UniswapV3.Factory.getPool(a, b, fee)` takes a `uint24` fee. On a local fork of Base at block 52965000 with the current contracts, `fee = 500` returns the WETH/USDC pool and `fee = 500 + 2**24` reverts with no data. For an output, `UniswapV3.Pool(target).fee()` returns 500 for that pool and reverts for an address whose `fee()` returns a number wider than 24 bits.

**Pass a signed value to a signed input.** `UniswapV4.StateView.getTickLiquidity(poolId, tick)` takes an `int24` tick, and `-1` asks for tick −1. The constant `~0` is the bit pattern of −1 and compiles there to the same program as `-1`. A `Uint256` with all bits set keeps its type and is not `-1` there. As a constant it is refused: passing the decimal literal `115792089237316195423570985008687907853269984665640564039457584007913129639935` as the tick fails with "constant Uint256 value is outside Int256's range; conversions check bounds, so choose a representable value or a type that includes it". Take the tick as an `Int256`:

```ts signed-input.sauce.ts theme={null}
function main(poolId: Uint256, tick: Int256): Uint256 {
  const [gross, net] = UniswapV4.StateView.getTickLiquidity(poolId, tick);
  return gross;
}
```

As a `Uint256` parameter holding 2²⁵⁶ − 1, the call reverts with no data on the same fork, and `Int256.fromBits(tick)` passes the same bits as `-1`.

**Containers keep their kind.** An array for an `intN[]` parameter is an `Int256[]`, and one for `uintN[]` is a `Uint256[]`. `BalancerV2.Vault.batchSwap` takes `limits` as `int256[]` and returns the `int256[]` of asset deltas:

```ts containers.sauce.ts theme={null}
// Calls Balancer V2's batchSwap with no swaps; the deltas of the two assets are both zero.
function main(): Int256[] {
  const swaps: { poolId: Uint256, assetInIndex: Uint256, assetOutIndex: Uint256, amount: Uint256, userData: bytes }[] = [];
  const funds = { sender: pot, fromInternalBalance: false, recipient: pot, toInternalBalance: false };
  const limits: Int256[] = [0, 0];
  return BalancerV2.Vault.batchSwap(0, swaps, [WETH, USDC], funds, limits, ctx.timestamp() + 60);
}
```

On the same fork the program returns `[0, 0]`. A scalar conversion does not retype an array, so a container of the other kind is refused:

| Mistake | Compiler message | Write instead |
| - | - | - |
| `const limits: Uint256[] = [0, 0];` passed as `limits` | "cannot convert Uint256 to Int256 without losing its numeric value" | `const limits: Int256[] = [0, 0];` |
| `function main(): Uint256[]` returning the deltas | "a return value, array element: expected Uint256, found Int256; use the matching integer kind in the container annotation, or convert scalar elements with `Int256(value)` or `Uint256(value)` (conversions check bounds); arrays and tuples cannot be retyped" | `function main(): Int256[]` |

[Builtins](https://docs.eco.com/sauce/builtins.md) lists `Int256`, `Uint256` and `Math.signExtend`.

## Booleans

`bool` holds `true` or `false`. Comparisons and `!`, `&&` and `||` produce one. A bool is kept apart from numbers: `filled + 1`, with `filled: bool`, fails with "a binary operator: expected a number, found a bool". Write `filled ? 2 : 1` to turn a bool into a number. In the other direction, `if`, `while`, `!`, `&&`, `||` and the first operand of `? :` accept a number as well as a bool, and treat any nonzero number as true.

A function with no return annotation returns a `Uint256`, even when it returns a comparison: annotate a helper that answers yes or no with `: bool`, and one that returns a signed value with `: Int256`. [Functions](https://docs.eco.com/sauce/functions.md#return-types) shows the rule.

## Addresses

An `Address` is a number. It compares with `==` and `!=`, and with `<` and `>` (a pool's `token0` is the lower address). A token symbol and a protocol contract are addresses:

```ts address.sauce.ts theme={null}
function main(recipient: Address): bool {
  return recipient != 0 && recipient != USDC;
}
```

On Solana an address holds a 32-byte public key; on an EVM chain, a 20-byte address. An address the chain's registry records is written by its name (see [Names](https://docs.eco.com/sauce/names.md)); any other address is a `main` parameter.

## Bytes and strings

`bytes` and `string` are the same type: a byte string. `"text"` is its UTF-8 bytes, and `` hex`00ff` `` raw bytes (an even number of hex digits, an optional `0x`, spaces ignored).

```ts bytes.sauce.ts theme={null}
function main(to: Address, amount: Uint256): bytes {
  const prefix = hex`00ff`;
  const packed = prefix.concat(to.toBytesBe(32), amount.toBytesBe(32));
  require(packed.length == 66, "bad length");
  require(packed[1] == 0xff, "bad prefix");
  require(packed.slice(34, 66).toBe() == amount, "bad amount");
  return packed;
}
```

| Method | On | Returns |
| - | - | - |
| `b.length` | bytes | the byte count |
| `b[i]` | bytes | byte `i`, as a number |
| `b.slice(start, end)` | bytes | bytes `start` up to, not including, `end`; `end < start` reverts |
| `b.concat(b1, b2, …)` | bytes | the bytes joined |
| `b.toBe()`, `b.toLe()` | at most 32 bytes | the bytes read as a big-endian or little-endian number |
| `x.toBytesBe(width)`, `x.toBytesLe(width)` | a number | its low `width` bytes (1 to 32), big-endian or little-endian |

Little-endian is Solana's layout: `amount.toBytesLe(8)` is a `u64` field.

A method can be called on a literal as well as on a variable:

```ts literal-method.sauce.ts theme={null}
function main(data: bytes): bytes {
  return hex`00ff`.concat(data);
}
```

This compiles for each chain. On a local fork of Base at block 52965000 with the current contracts, `data = 0x1234` returns `0x00ff1234`.

Two bytes values cannot be compared with `==`: `a == b` fails with "a comparison operator requires a scalar value". Compare their hashes:

```ts bytes-hash.sauce.ts theme={null}
function main(a: bytes, b: bytes): bool {
  return crypto.keccak256(a) == crypto.keccak256(b);
}
```

`abi.encode` and `abi.decode` convert between values and ABI bytes on EVM chains. See [Builtins](https://docs.eco.com/sauce/builtins.md).

## Arrays

An array holds elements of one type. Its length is fixed when it is created.

```ts arrays.sauce.ts theme={null}
function main(amounts: Uint256[]): Uint256 {
  let doubled = new Array(amounts.length);
  for (let i = 0; i < amounts.length; i++) {
    doubled[i] = amounts[i] * 2;
  }
  const fees = [100, 500, 3000];
  let none: Uint256[] = [];
  return doubled[0] + fees[1] + none.length;
}
```

* `new Array(n)` makes an array of `n` zeros, to fill by index. There is no `push`.
* `[a, b, c]` is an array literal; every element must have the same type. An empty literal needs a declared type: `let none: Uint256[] = [];`.
* `xs.length` is the element count and cannot be assigned. An index past the end reverts: on a local fork of Base at block 52965000 with the current contracts, `xs[5]` on the two-element argument `[1,2]` reverts with no data.
* `T[][]` nests arrays. A `main` parameter takes at most 4 levels.
* One caller-supplied collection in `main`'s arguments is limited to 16 KiB of elements: a `Uint256[]` takes up to 512. Oversized input reverts the cook; split a larger batch into separate cooks, each atomic independently. On a local fork of Base at block 53161500 with the current contracts, a `Uint256[]` argument of 512 elements was accepted and one of 513 reverted with no data, with `main` only returning its length. On Solana 513 elements are refused too, but one transaction's 1,400,000 compute units run out first: simulated with the `sauce` command's local copy of Solana (LiteSVM) and the current engine and Kitchen, the same length-only program used 1,149,685 compute units for 256 elements, and 384 and 512 elements exhausted the budget. Where `main` does more than return a length, the usable batch is smaller: simulate the largest batch you will send.
* Do not use an unread array element as a required validation step. A call-free array literal indexed at a literal position (or at a `const` bound to one) evaluates only the element it takes: `function main(zero: Uint256): Uint256 { return [1 / zero, 7][1]; }` returns `7` when `zero` is `0`, where `return 1 / zero;` reverts, and an overflow in the unread element (`[big * 2, 7][1]`, with `big` a `Uint256` of 2²⁵⁵ or more) returns `7` too. Bound to a variable first (`const a = [1 / zero, 7]; return a[1];`), indexed at run time, at a `let` or at a computed position such as `0 + 1`, the division reverts, as does an unused `const x = 1 / zero;`. All were simulated on a local fork of Base at block 53161500 with the current contracts; on Solana `[1 / zero, 7][1]` also returns `7`.
* **A call in the literal changes that.** If a literal contains a helper call, a contract call or a token read (`pot.USDC`, `USDC.balanceOf(pot)`), every element up to and including the last one runs, in order, even when indexing takes another element. `[1 / zero, f()][1]` and `[f(1 / zero), 7][1]` both revert for `zero = 0`, and a write inside `f` runs when execution reaches it. Elements after the last call are still skipped: `[pot.USDC, 1 / zero][0]` returns the Pot's USDC, and `[f(1), 1 / zero][0]` returns `f(1)`. A builtin such as `Math.mulDiv` is not a call. Simulated on a local fork of Base at block 53166500 with the current contracts. Use an explicit `require` for a condition that must always be checked, and a statement of its own for a call that must run.
* An array is a reference. Assigning it to another variable, or passing it to a function, shares the same elements: after `const b = a; b[0] = 7;`, `a[0]` is `7` (simulated on the fork at block 52965000).

A module-level `const` array is a lookup table, read only at a literal index. See [Layout of a program](https://docs.eco.com/sauce/program-layout.md#module-level).

## Records

A record holds named fields, each with its own type. Its type is an `interface` or an inline object type:

```ts records.sauce.ts theme={null}
interface Leg {
  pool: Address;
  amount: Uint256;
}

function main(legs: Leg[], to: Address): Uint256 {
  const order = { amount: legs[0].amount, recipient: to };
  order.amount = order.amount + 1;
  return order.amount + legs[0][1];
}
```

* `{ amount, recipient: to }` builds a record; a field named like a variable can be written once (`amount`).
* A field is read by name: `order.amount`. This works on every record.
* A field is also read by a constant position, `legs[0][1]` for the second field of the first leg, but only on a record the compiler does not hold. A record that comes from a `main` parameter, an array element, a contract call, `abi.decode` or a function's return value is read by position. A position is a number literal or a `const` bound to one:

```ts positions.sauce.ts theme={null}
function main(order: { amount: Uint256, fee: Uint256 }): Uint256 {
  const second = 1;
  return order[0] + order[second];
}
```

* An object literal bound to a variable in the same function is held by the compiler under its field names. Reading it by position is refused, even when some fields are run-time values: `order[0]` on `const order = { amount: amount, fee: 30 };` fails with "a computed member of an object the compiler holds (read it by name: `object.member`) is not supported in SauceScript yet". Write `order.amount`.

* A position that is not a literal or a bound `const` is refused on a record that comes from a parameter: `order[i]` fails with "a tuple index must be a number literal or a `const` bound to one". Read the field by name, or branch on `i` between literal positions: `i == 0 ? order[0] : order[1]`. A computed position on an object literal is refused with the message of the previous item.

* `const [a, b] = record` destructures by position, and works on an object literal too. See [Expressions and control flow](https://docs.eco.com/sauce/expressions-and-control-flow.md#destructuring).

* A record is a reference, like an array.

A contract call with several outputs returns a record whose fields are named after the ABI's outputs: `UniswapV3.Pool(pool).slot0().sqrtPriceX96`.

## Accounts

On Solana, a program reads and writes the accounts its transaction attaches. Each is a `main` parameter of type `Account`, optionally with the roles the transaction must give it:

```ts accounts.solana.sauce.ts theme={null}
function main(state: Account<Writable, Signer>, mint: Account, vault: Account<Writable>): Uint256 {
  require(mint.address != vault.address, "mint is the vault");
  require(state.owner == ctx.self(), "state is not the engine's");
  return mint.dataLen;
}
```

* `acct.address` is the account's public key, `acct.owner` the program that owns it, `acct.dataLen` its data size in bytes. They are read-only.
* Accounts compare with `==` and `!=` only.
* An account is not data. It cannot be returned, and arithmetic on it fails:

| Mistake | Compiler message | Write instead |
| - | - | - |
| `return mint;` from `main(mint: Account): Account` | "a return value can't be an account — an account is not data, so it can't be used as a value here" | `return mint.address;`, with the return type `Address` |
| `mint + 1` | "a binary operator: expected a number, found an account" | arithmetic on `mint.address` or `mint.dataLen` |

```ts account-key.solana.sauce.ts theme={null}
function main(mint: Account): Address {
  return mint.address;
}
```

* `svm.sstore` writes only an account declared `Account<Writable, Signer>`.

On an EVM chain, a bare `Account` compiles as an address, and `acct.address` is that address. A role is refused: `function main(target: Account<Writable>) {}` fails with "`an Account role` is not available on the evm target; an `Account` is an address value on the EVM — write `Account` alone". Write the bare type:

```ts account-evm.sauce.ts theme={null}
function main(target: Account): Address {
  return target.address;
}
```

[EVM and Solana](https://docs.eco.com/sauce/targets.md) describes how accounts are attached.

## Type names that are not types

A Solidity or JavaScript type name is refused with the list of valid ones. `function main(amount: uint256) {}` fails with:

```text theme={null}
`uint256` is not a type — write `Uint256`, `Address`, `Account`, `bool`, `bytes` or `string`, an array `T[]`, an inline object type `{ a: Uint256 }` for a tuple (or the name of an `interface` this module declares or `import`s), or `void` for a function that returns nothing
```

Write `amount: Uint256`. Union types (`Uint256 | Address`), `Array<T>` and tuple types (`[Uint256, Address]`) are refused the same way.

## Not supported

| Not supported | Write instead |
| - | - |
| `null`, `undefined` | `0`, or a `bool` flag |
| fractions, `1.5`, `1e18` | integers in the smallest unit; `10 ** 18` |
| `as` casts, `satisfies`, `x!` | annotations; a value of the right type; `Int256(x)` and `Uint256(x)` to change an integer's kind |
| `enum` | module constants |
| `type` aliases | an `interface`, or an inline object type |
| `class` | records and functions |
| union types, `Array<T>`, tuple types | `T[]`, records |
| comparing bytes with `==` | `crypto.keccak256` of each |
| template literals | `"text"`, or bytes built with `.concat` |

Each of these fails the compile. A `type` alias such as `type Order = { amount: Uint256 };` is reported as "a `type` alias declaration (an `interface` names a record type) is not supported in SauceScript"; declare an `interface`, as `records.sauce.ts` does. An `as` cast such as `value as Address` is reported as "an `as` cast is not supported in SauceScript". An address is already a number, so returning `value` from a function declared `: Address` compiles.

## Next steps

* [Expressions and control flow](https://docs.eco.com/sauce/expressions-and-control-flow.md): operators on these types, signed arithmetic, loops and destructuring.
* [Functions](https://docs.eco.com/sauce/functions.md): passing values by reference and by copy, and the `: Int256` return annotation.
* [Builtins](https://docs.eco.com/sauce/builtins.md): `Math`, `crypto`, `abi` and every value method.
* [EVM and Solana](https://docs.eco.com/sauce/targets.md): accounts and what differs between targets.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.