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

# @eco-incorp/sauce-compiler API

> The API of the SauceScript compiler that @eco-incorp/sauce ships, for code that compiles programs without the sauce command.

`@eco-incorp/sauce-compiler` compiles SauceScript to Sauce bytecode. It is the Rust compiler built to WebAssembly, with no file system access: you pass a synchronous `resolve` function that returns each module's bytes. `@eco-incorp/sauce` pins it at exactly 2.6.1, the version this page documents, and re-exports it from `@eco-incorp/sauce/compiler`. Import the compiler from there.

> [!WARNING]
>
> Compiler output runs on the engine releases recorded for its exact compiler version, and `@eco-incorp/sauce` pins the compiler with that release set. A program compiled by any other compiler version may not run on the deployed engines. Do not install `@eco-incorp/sauce-compiler` separately for programs you send. The pairing is listed in [release sets and compiler pairing](https://docs.eco.com/sauce-sdk/api/deployments/releases.md).

## What `@eco-incorp/sauce/compiler` exports

| Name | From `@eco-incorp/sauce/compiler` |
| - | - |
| `compile` | Yes. The compiler's `compile`, behind a check that its WebAssembly is initialized |
| `ready` | Yes. Resolves to nothing once the compiler can run; see [In a browser](#in-a-browser) |
| `CompileOptions`, `CompileResult`, `ResolveModule`, `ResolvePackage`, `SauceTarget`, `AccountEntry`, `AccountManifest`, `AccountRole`, `WasmSource` | Yes, as types |
| `resolveSdkModule`, `resolveSdkPackage` | Yes. The SDK's [module resolvers](https://docs.eco.com/sauce-sdk/api/compiler/resolve.md) |
| `ChainProfile` | No. Derive it: `NonNullable<CompileOptions["chainProfiles"]>[string]` |
| `AbiParameter`, `EntrySignature`, `ProgramReport` | No. Derive them: `CompileResult["entryParams"][number]`, `CompileResult["entrySignature"]`, `CompileResult["programs"][number]` |
| `CompileError`, `Diagnostic`, `DiagnosticLabel`, `SourcePosition` | No. Declare the shape shown under [Errors](#errors) locally |
| `clearFunctionCache`, `functionCacheStats`, `FnCacheStats` | No. See [Function cache](#function-cache) |

The subpath has no argument codec: every program takes ABI-encoded arguments (see [Arguments](#arguments)).

## Compile a program in Node.js

Prerequisites: Node.js 24 or newer, and `@eco-incorp/sauce` installed in an ES module project. Save the program as `src/main.sauce.ts`:

```ts src/main.sauce.ts theme={null}
function main(amount: Uint256): Uint256 {
  return amount + 1;
}
```

Then compile it:

```typescript compile.ts theme={null}
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { compile } from "@eco-incorp/sauce/compiler";

const root = resolve("src");
const result = compile({
  target: "evm",
  entry: "main.sauce.ts",
  resolve: (path) => {
    try {
      return readFileSync(resolve(root, path));
    } catch (error) {
      // Nothing at this path: the compiler may try another candidate.
      if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
      throw error;
    }
  },
});
console.log(result.bytecode.length, result.isaRevision, result.entryEncoding);
console.log(JSON.stringify(result.entryParams));
console.log(JSON.stringify(result.entrySignature));
```

```bash theme={null}
node --experimental-strip-types compile.ts
# 38 stack-v3 abi-v1
# [{"name":"amount","type":"uint256"}]
# {"params":[{"name":"amount","type":"Uint256","abi":{"name":"amount","type":"uint256"}}],"returns":"Uint256"}
```

`bytecode` is the program without its arguments. `main`'s arguments are ABI-encoded from `entryParams` and appended to it. To compile a program for a chain by name, with its token symbols, protocol namespaces and `@sauce/token` in scope, use [`routes.compileSauceProgram`](https://docs.eco.com/sauce-sdk/api/routes/compile) instead.

## `compile`

```typescript theme={null}
export function compile(options: CompileOptions): CompileResult;
```

Compiles a SauceScript program to Sauce bytecode. It is synchronous. Validation, compile and resolve failures throw an [error with diagnostics](#errors).

### `CompileOptions`

```typescript theme={null}
export interface CompileOptions {
    target: SauceTarget;
    resolve: ResolveModule;
    resolvePackage?: ResolvePackage;
    entry?: string;
    entryKind?: "module" | "closure";
    contracts?: Array<[name: string, path: string]>;
    inlineContracts?: Array<[name: string, abi: string]>;
    ambient?: Array<string>;
    defines?: Array<[name: string, value: string]>;
    args?: Array<bigint | boolean | string>;
    optimize?: boolean;
    chain?: string;
    chainProfiles?: Record<string, ChainProfile>;
    cache?: boolean;
}
```

| Option | Default | Meaning |
| - | - | - |
| `target` | Required | The engine the program is compiled for: `"evm"` or `"svm"`. |
| `resolve` | Required | Returns a module's bytes by path. See [`ResolveModule`](#resolvemodule). |
| `resolvePackage` | None | Maps a bare specifier such as `"@sauce/token"` to a path `resolve` accepts. See [`ResolvePackage`](#resolvepackage). |
| `entry` | `"main.js"` | The entry module, the first path `resolve` is asked for. |
| `entryKind` | `"module"` | How the entry's text is read: a SauceScript module, or `"closure"`, one function expression. See [Closure entries and bound arguments](#closure-entries-and-bound-arguments). |
| `contracts` | None | `[name, path]` pairs declaring contracts whose ABI the resolver serves by path. Each resolves as `import Name from "./path.json"` would and emits the same bytes. |
| `inlineContracts` | None | `[name, abi]` pairs declaring contracts whose ABI JSON is given as a string. Applied to the entry module only. A name that collides with a `contracts` entry, an `import`, another `inlineContracts` entry or a declared function is a compile error. |
| `ambient` | None | Module specifiers whose exported functions and constants are in scope in every module with no `import`. Each resolves like an `import` of it. User code wins over an ambient name: a name the module declares, imports or binds blocks the ambient one. Two ambient modules exporting the same name is a compile error, and so is an ambient module that defines `main`. An absolute path, a per-target file (`./token.evm.js`), a subpath or a `.json` file is refused. |
| `defines` | None | `[name, value]` pairs injected into every module as module-scope `const`s, so a `const`-gated branch is decided at compile time. A value is a decimal or `0x` integer string (256 bits), with an optional trailing `n` and `_` separators. A define replaces a module's own `const` of that name, in every module, including packages you did not write. It cannot replace a `let`, a function, a lookup table, a bound contract, a JSON import or an import alias; each is a compile error. The value must be assignable to the declaration's type, and a repeated name is an error. |
| `args` | None | One value per argument of `main`, in `entryParams` order, compiled in as constants. See [Closure entries and bound arguments](#closure-entries-and-bound-arguments). |
| `optimize` | `true` | Enables optimizations. Validation runs when `false` too. |
| `chain` | None | The key in `chainProfiles` whose profile the entry program is compiled against. Echoed in `CompileResult.chain`. A key `chainProfiles` lacks is a compile error. |
| `chainProfiles` | None | Each chain's [builtin profile](#chain-profiles), keyed by a chain id or name of your choice. Every profile is validated, whether or not the compile uses it. A program built with `program(closure, { chain })` is compiled against the profile its options name. |
| `cache` | `true` | The [per-function lowering cache and module cache](#function-cache). `false` forces a fresh compile. |

### `CompileResult`

```typescript theme={null}
export interface CompileResult {
    bytecode: Uint8Array;
    isaRevision: "stack-v3";
    entryEncoding: "abi-v1";
    manifest?: AccountManifest;
    chain?: string;
    entryParams: AbiParameter[];
    entrySignature: EntrySignature;
    programs: ProgramReport[];
}

interface AbiParameter {
    name: string;
    type: string;
    components?: AbiParameter[];
    indirect?: true;
}

interface EntrySignature {
    params: { name: string; type: string; abi?: AbiParameter }[];
    returns?: string;
}

interface ProgramReport {
    id: number;
    path: number[];
    target: SauceTarget;
    chain?: string;
    bytecode: Uint8Array;
    manifest?: AccountManifest;
    captures: string[];
}
```

| Field | Meaning |
| - | - |
| `bytecode` | The program, without its arguments. |
| `isaRevision` | The instruction-set identifier, independent of the package version. It does not say which engine release runs the program; the exact compiler version does (see [release sets and compiler pairing](https://docs.eco.com/sauce-sdk/api/deployments/releases.md)). |
| `entryEncoding` | Always `"abi-v1"`: the arguments are the ABI encoding of `entryParams`. |
| `manifest` | The Solana account manifest. `undefined` on the EVM. |
| `chain` | The `chain` option, when the compile named one. |
| `entryParams` | `main`'s parameters as an ABI parameter list, in the JSON shape viem, ethers and Solidity share. A parameter bound with `args` and an SVM `Account` parameter are absent. `Address` is `address` on the EVM and `bytes32` on Solana. |
| `entrySignature` | `main`'s signature in SauceScript's own type names (`Uint256`, `Int256`, `Address`, `Account`, records, arrays), with each encoded parameter's `abi` entry. It keeps parameters bound with `args` and SVM accounts. `returns` is the declared return type, `"void"` for a void `main`, and absent for a `main` with no return annotation that returns a value. |
| `programs` | Each closure compiled as a program of its own with `program(closure, ...args)`, in the order each finished, a nested program before the one around it. See [Programs built in a program](#programs-built-in-a-program). |

## Arguments

Every program takes ABI-encoded arguments (`entryEncoding: "abi-v1"`). Encode the values with viem's `encodeAbiParameters(result.entryParams, values)` and append them to `bytecode`; that payload is the `program` of `Pot.cook(engine, program)`.

```typescript arguments.ts theme={null}
import { encodeAbiParameters, toBytes } from "viem";
import { compile } from "@eco-incorp/sauce/compiler";

const source = new TextEncoder().encode(
  "function main(amount: Uint256, recipient: Address): Uint256 {\n  return amount + 1;\n}\n",
);
const result = compile({ target: "evm", resolve: (path) => (path === "main.js" ? source : undefined) });

const args = encodeAbiParameters(result.entryParams, [42n, "0x1111111111111111111111111111111111111111"]);
const program = new Uint8Array([...result.bytecode, ...toBytes(args)]);
console.log(result.entryParams.map((p) => `${p.type} ${p.name}`).join(", "), program.length - result.bytecode.length);
```

```text theme={null}
uint256 amount, address recipient 64
```

A top-level tuple of fixed size is marked `indirect: true`: encode it as a dynamic parameter, with its head word holding the byte offset of its encoding. With no `indirect` parameter, `encodeAbiParameters(entryParams, values)` is the payload as it stands. A named type the entry module imports is laid out from its fields' types alone, so its `Address` and `Account` fields read as `uint256` and its `string` fields as `bytes`.

`routes.encodeEntryArguments(program, values)` from `@eco-incorp/sauce` checks values against `main`'s declared types and encodes them for a program compiled with `routes.compileSauceProgram`; see [routes](https://docs.eco.com/sauce-sdk/api/routes/compile.md).

## Closure entries and bound arguments

`entryKind: "closure"` reads the entry as one function expression, exactly as `Function.prototype.toString` prints it: `() => { … }`, `() => expr` or `function () { … }`. Its parameters and return annotation are `main`'s, its block is `main`'s body, and a concise body is `main`'s `return` (a concise body with no value makes `main` void). A `function` declared at the top of its block is a module function. Diagnostics are located in the closure text. An `async` or generator closure, text that is more than one function expression, and a named `function` expression that calls itself are refused. Imported modules are always modules. The compiler reads the text; it runs no JavaScript and captures no host variables.

`args` compiles `main` for given argument values: each parameter becomes a `const` of its value, so a branch on it compiles only its taken arm, calls passing it are specialized, and nothing is decoded from the program's input. Bound parameters disappear from `entryParams` and stay in `entrySignature`. Each value is one word: a non-negative `bigint` below 2^256, a `boolean`, or a decimal or `0x` integer string. Pass one value per parameter (except an SVM `Account`, which stays an attached account). Bytes, arrays and records cannot be bound. For a negative `Int256`, pass its two's-complement word: `(1n << 256n) - 10n` is `-10`.

```typescript bound.ts theme={null}
import { compile } from "@eco-incorp/sauce/compiler";

const closure = new TextEncoder().encode("(amount: Uint256, double: bool): Uint256 => double ? amount * 2 : amount");
const options = { target: "evm" as const, entryKind: "closure" as const, resolve: () => closure };

const open = compile(options);
const bound = compile({ ...options, args: [21n, true] });

console.log(open.entryParams.length, open.bytecode.length);
console.log(bound.entryParams.length, bound.bytecode.length);
console.log(JSON.stringify(bound.entrySignature));
```

```text theme={null}
2 59
0 16
{"params":[{"name":"amount","type":"Uint256","abi":{"name":"amount","type":"uint256"}},{"name":"double","type":"bool","abi":{"name":"double","type":"bool"}}],"returns":"Uint256"}
```

## Programs built in a program

Inside SauceScript, `program(closure, ...args)` compiles the closure as a separate program and returns `{ code, args }`. Each argument binds one of the closure's parameters; an optional final literal `{ chain, target }` selects its profile and target. `CompileResult.programs` reports each one: `id` (unique in the compile), `path` (the ids of the programs it is nested in, outermost first), `target`, `chain`, `bytecode`, `manifest`, and `captures`, the names of the values it reads from the program around it, in the order their 32-byte words appear in `args`. The [builtins reference](https://docs.eco.com/sauce/builtins.md) covers `program` in SauceScript.

```typescript programs.ts theme={null}
import { compile } from "@eco-incorp/sauce/compiler";

const source = new TextEncoder().encode(
  [
    "function main(fee: Uint256): Uint256 {",
    "  const parcel = program((amount: Uint256): Uint256 => amount - fee, 1000);",
    "  return parcel.args.length;",
    "}",
    "",
  ].join("\n"),
);
const result = compile({ target: "evm", resolve: (path) => (path === "main.js" ? source : undefined) });
for (const p of result.programs) console.log(p.id, JSON.stringify(p.path), p.target, p.bytecode.length, JSON.stringify(p.captures));
```

```text theme={null}
0 [] evm 51 ["fee"]
```

The bound argument `1000` folds into the inner program's code; `fee`, a run-time value of the outer program, is captured as a word.

## Resolving modules

### `ResolveModule`

```typescript theme={null}
export type ResolveModule = (path: string) => Uint8Array | undefined | null;
```

Returns the bytes at `path` (a Node.js `Buffer` is a `Uint8Array`). `undefined` or `null` means nothing is at that path, and the compiler falls back: after `token.evm.ts` misses, it asks for `token.ts`. A throw means the path names something that could not be read; it propagates and the compile fails, so a miss must return `undefined` rather than throw. Any other return value is a resolve error. Serve every `.json` path a program names: the compiler asks for one either as a contract ABI or as data imported `with { type: "json" }`.

### `ResolvePackage`

```typescript theme={null}
export type ResolvePackage = (specifier: string, importer: string) => string | undefined | null;
```

Maps a bare specifier to a path `resolve` accepts. `importer` is the resolved path of the importing module. `undefined` or `null` declines, and the specifier is used as a path; a throw means the package is not installed. `resolveSdkPackage` and `resolveSdkModule` serve the SDK's SauceScript libraries (`@sauce/token`, `@sauce/router`) and ABIs to a compile (see [SDK module resolvers](https://docs.eco.com/sauce-sdk/api/compiler/resolve.md)).

## Errors

`compile` throws an `Error` that carries `diagnostics`. The compiler declares it as `CompileError`; `@eco-incorp/sauce/compiler` does not export that type, so declare the shape where you need it:

```typescript theme={null}
interface CompileError extends Error {
    diagnostics: Diagnostic[];
}

interface Diagnostic {
    path: string;
    message: string;
    start: SourcePosition;
    end: SourcePosition;
    secondary?: DiagnosticLabel;
    code?: string;
}

interface DiagnosticLabel {
    message: string;
    start: SourcePosition;
    end: SourcePosition;
}

interface SourcePosition {
    offset: number;
    line: number;
    column: number;
}
```

`message` is the rendered report: the rule broken, the source line and a caret under the span. `diagnostics` places each source error, in source order. `code` names the error (`"UnknownIdentifier"`, `"UnknownFunction"`, `"TypeMismatch"`) and is stable across releases, so a host can tell refusals apart without matching `message`; it is absent for a parse or subset error. `path` is the module as `resolve` was asked for it, so an error in an imported module names that module. `line` and `column` are 1-based; `offset` and `column` count UTF-16 code units, so `source.slice(start.offset, end.offset)` is the span. The `--> path:line:col` header inside `message` counts characters instead, which differs on a line with a character outside the Basic Multilingual Plane. An error with no source location, such as an invalid option, a resolver failure or a builtin a chain profile refuses, has an empty `diagnostics` array. A message thrown by your `resolve` is preserved.

```typescript errors.ts theme={null}
import { compile } from "@eco-incorp/sauce/compiler";

// The shape of what compile throws (CompileError), declared locally: the subpath does not export it.
type CompileError = Error & {
  diagnostics: { path: string; message: string; code?: string; start: { line: number; column: number; offset: number } }[];
};

const source = "function main(): Uint256 {\n  return missing + 1;\n}\n";
try {
  compile({
    target: "evm",
    resolve: (path) => (path === "main.js" ? new TextEncoder().encode(source) : undefined),
  });
} catch (error) {
  const { message, diagnostics } = error as CompileError;
  console.log(message);
  for (const d of diagnostics) console.log(d.code, d.path, d.start.line, d.start.column, d.message);
}
```

```text theme={null}
compiling the program failed
│
├─▶ compilation failed
│
╰─▶ `missing` is not a declared variable
    ├╴`missing` is not a declared variable (bytes 36..43)
    ╰╴error: `missing` is not a declared variable
       --> main.js:2:10
        |
      2 |   return missing + 1;
        |          ^^^^^^^
UnknownIdentifier main.js 2 10 `missing` is not a declared variable
```

## Chain profiles

```typescript theme={null}
interface ChainProfile {
    name?: string;
    builtins?: Record<string, "unsupported" | { constant: string }>;
}
```

`ChainProfile` is not exported from `@eco-incorp/sauce/compiler`; it is the value type of `CompileOptions["chainProfiles"]`. A target is a VM, and two EVM chains can run the same engine while a builtin behaves differently on them. A profile tells the compiler what a chain does with each target-gated builtin, keyed by its source spelling (`"ctx.prevrandao"`, `"evm.create2"`):

* `"unsupported"`: a program that uses the builtin fails to compile for that chain, instead of reverting there.
* `{ constant: "0" }`: the chain answers with this value on every call, so it compiles to the literal and constant folding applies. Decimal or `0x` hex, as a string. Only for a builtin that takes no arguments.

A builtin the profile does not name compiles as it does without a profile. A name that is not a configurable builtin, such as a typo or `ctx.timestamp`, is a compile error. `name` is used in the error message; without it the message uses the key.

```typescript profiles.ts theme={null}
import { compile, type CompileOptions } from "@eco-incorp/sauce/compiler";

type ChainProfile = NonNullable<CompileOptions["chainProfiles"]>[string];

const source = "function main(): Uint256 {\n  return ctx.prevrandao();\n}\n";
const chainProfiles: Record<string, ChainProfile> = {
  "1": { name: "Ethereum" },
  "42161": { name: "Arbitrum", builtins: { "ctx.prevrandao": "unsupported" } },
};
const options: CompileOptions = {
  target: "evm",
  resolve: (path) => (path === "main.js" ? new TextEncoder().encode(source) : undefined),
  chainProfiles,
};

const onEthereum = compile({ ...options, chain: "1" });
console.log(onEthereum.chain, onEthereum.bytecode.length);
try {
  compile({ ...options, chain: "42161" });
} catch (error) {
  console.log((error as Error).message);
}
```

```text theme={null}
1 15
compiling the program failed
│
├─▶ compilation failed
│
╰─▶ `ctx.prevrandao()` is not available on Arbitrum
```

## Solana account manifest

On `target: "svm"`, `CompileResult.manifest` lists the accounts a caller attaches, by index.

```typescript theme={null}
export type AccountRole = "readonly" | "writable" | "readonly_signer" | "writable_signer";

export type AccountEntry =
    | { kind: "slot"; index: number; name: string; requires: AccountRole }
    | { kind: "remaining"; from: number };

export interface AccountManifest {
    accounts: AccountEntry[];
}
```

A `slot` is an `Account` parameter of `main`: the engine reads it at `index`, `name` is the parameter's name, and `requires` is the role the attached account must have. The roles are Solana's four `AccountRole` values, so an SDK maps one onto an `AccountMeta` directly; a bare `Account` is `"readonly"`. A role is the program author's assertion, as an ABI declaration is: the compiler does not infer what a program called by CPI does with an account. `remaining` marks where the caller's own variable-length accounts begin (`from` is the index of the first). It appears only when the program reads them (`svm.remainingAccount` or `svm.remainingAccounts`), and it is always last.

## Function cache

With `cache: true` (the default), the compiler keeps two stores global to the WebAssembly instance. The lowering cache stores each declared function's lowering, keyed on the function's source text, the target, and every external binding it consulted: defines, imported constants and JSON tables, contract ABIs, callee signatures and its chain profile's setting of each builtin it reads. Recompiling a program with different `defines` reuses every function a changed define does not affect. The module cache keeps each parsed module by its path and exact text, and each contract ABI by its name, path and exact JSON, bounded at 64 modules and 64 MiB of text, and 4,096 ABIs and 16 MiB of JSON. A program compiles to the same bytes either way. Each store empties itself when full. `cache: false` on a compile forces a fresh one.

```typescript theme={null}
function clearFunctionCache(): void;
function functionCacheStats(): FnCacheStats;

interface FnCacheStats {
    hits: number;
    misses: number;
    inserts: number;
}
```

`clearFunctionCache()` empties both stores and resets the lowering cache's counters; `functionCacheStats()` reads them. Neither touches a cache your resolver keeps. `@eco-incorp/sauce/compiler` does not re-export them. They belong to the pinned compiler package, which `@eco-incorp/sauce` installs under the alias `sauce-compiler`. npm places that alias in your project's `node_modules`, so the import below resolves there; a package manager that does not expose a dependency's dependencies, such as pnpm, does not resolve it. It is the same pinned compiler and the same WebAssembly instance the SDK compiles with, not a separate install.

```typescript cache.ts theme={null}
import { compile } from "@eco-incorp/sauce/compiler";
import { clearFunctionCache, functionCacheStats } from "sauce-compiler";

const source = new TextEncoder().encode("function main(): Uint256 {\n  return 1;\n}\n");
const options = { target: "evm" as const, resolve: (path: string) => (path === "main.js" ? source : undefined) };

clearFunctionCache();
compile(options);
compile(options);
console.log(functionCacheStats());
```

```text theme={null}
{ hits: 1, misses: 1, inserts: 1 }
```

## In a browser

```typescript theme={null}
export type WasmSource = ArrayBuffer | ArrayBufferView | { href: string } | string;

export declare function ready(source?: WasmSource): Promise<void>;
```

The browser build cannot load its WebAssembly synchronously. Call `await ready()` once before the first compile; until it resolves, every compile in a browser throws. In Node.js it resolves at once and `compile` works without it. Call it as often as you like: it initializes once, concurrent calls share one attempt, and a failed attempt is retried by the next call. With no `source`, the browser build fetches the WebAssembly from beside the compiler's own module, the URL a bundler rewrites to an asset. Pass the bytes (an `ArrayBuffer` or typed array) or a URL where that does not apply. The type accepts a `URL` through `{ href: string }`. Use Node.js or a bundler that supports package `exports` subpaths. The same `ready` is exported from `@eco-incorp/sauce`.

```typescript ready.ts theme={null}
import { compile, ready } from "@eco-incorp/sauce/compiler";

await ready();
const source = new TextEncoder().encode("function main(): Uint256 {\n  return 1;\n}\n");
const { bytecode } = compile({ target: "evm", resolve: (path) => (path === "main.js" ? source : undefined) });
console.log(bytecode.length);
```

## Next steps

* Compile a program for a chain by name, with its tokens and protocols in scope: [`routes.compileSauceProgram`](https://docs.eco.com/sauce-sdk/api/routes/compile).
* Which engine releases run this compiler's output: [release sets and compiler pairing](https://docs.eco.com/sauce-sdk/api/deployments/releases.md).
* The language the compiler accepts: [SauceScript builtins](https://docs.eco.com/sauce/builtins.md).
* What the bytecode is and how a Pot runs it: [Bytecode](https://docs.eco.com/sauce/bytecode.md).


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