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

# Compiling programs

> Compile SauceScript from TypeScript, and choose the function that resolves and returns what your code needs.

Three functions compile a SauceScript program. They differ in what they resolve and what they return.

| Function | Import | Resolves | Returns |
| - | - | - | - |
| `routes.compileSauceProgram` | `@eco-incorp/sauce` | The chain's token symbols, protocol namespaces and `chainId`, and the `@sauce/token` functions with no import | One program for one chain, with `main`'s parameters, the entry encoding and, on Solana, the account manifest. This is what `sauce compile` runs |
| `routes.compileSauceRoute` | `@eco-incorp/sauce` | The chain's token symbols and protocol namespaces | The program as one bytecode segment, plus one encoded cook call on the Pot you name |
| `compile` | `@eco-incorp/sauce/compiler` | Nothing: you serve every module | The compiler's own result |

Compiling reads no network, and the result is the same in Node and in a browser. In a browser, call [`ready()`](#ready) once first.

## routes.compileSauceProgram

Compiles a program for one chain.

### Import

```ts theme={null}
import { routes } from "@eco-incorp/sauce";
```

### Usage

```ts compile-program.ts theme={null}
import { routes } from "@eco-incorp/sauce";

const source = `
function main(to: Address, amount: Uint256) {
  require(pot.USDC >= amount, "not enough USDC");
  USDC.transfer(to, amount);
}
`;

const program = routes.compileSauceProgram("base", source);

console.log(program.chain.slug, program.chain.id);
console.log(program.entryEncoding, program.bytecode.length, "bytes");
console.log(program.entryParams);
console.log(program.manifest);
```

```text theme={null}
base 8453
abi-v1 223 bytes
[
  { name: 'to', type: 'Address' },
  { name: 'amount', type: 'Uint256' }
]
undefined
```

The source is a module that declares `function main(...)`, or a script whose top-level statements run as the entry. A script cannot `return`, so a program that returns a value declares `main`. The program reads the chain's names with no import: token symbols (`USDC`), protocol namespaces (`UniswapV3.Factory`), `chainId`, and the `@sauce/token` functions. One source therefore compiles unchanged for every chain where its names exist.

### Signature

```ts theme={null}
function compileSauceProgram(
  chain: ChainRef,
  source: string,
  options?: SauceProgramOptions,
): CompiledSauceProgram;

type ChainRef = number | bigint | string | CanonicalChain;

interface SauceProgramOptions extends SauceCompileOptions {
  readonly args?: readonly EntryValue[];
  readonly stops?: readonly string[];
}

interface SauceCompileOptions {
  target?: CompileTarget; // "v1" | "v12" | "svm"
  resolve?: ResolveModule;
  resolvePackage?: ResolvePackage;
  defines?: Record<string, bigint | boolean | number>;
  tokens?: Readonly<Record<string, AddressInput>>;
  contracts?: ContractsConfig;
  accessors?: boolean | ProtocolRewriteOptions;
  ambient?: string[];
}
```

### Parameters

| Parameter | Type | Meaning |
| - | - | - |
| `chain` | `ChainRef` | The chain: a slug (`"base"`), an alias, a numeric chain id (`8453`), or a chain record from `/chains` |
| `source` | `string` | The program's SauceScript source |
| `options` | `SauceProgramOptions` | Optional. The fields below |

| Option | Type | Default | Meaning |
| - | - | - | - |
| `args` | `readonly EntryValue[]` | none | `main`'s arguments, in order, bound into the program as constants: the compiler's own `args`. Each must be one word: a `Uint256` or `Int256` integer (a negative `bigint` for a negative `Int256`), an address or a `bool`; on Solana an `Account` parameter takes none. The program then takes no arguments at run time. A bound parameter is a constant of `main` alone, as the parameter was: one named like a module constant or one of the chain's values leaves that value as it is outside `main`. A define named like a bound parameter is refused |
| `stops` | `readonly string[]` | none | Solana only. The venue swaps at a price to compile in their stopped form, each named by a derivation from `stoppable` |
| `target` | `"v1" \| "v12" \| "svm"` | `"v12"` on an EVM chain, `"svm"` on Solana | The bytecode target. `"v1"` and `"v12"` both select the EVM backend |
| `resolve` | `ResolveModule` | none | Serves the program's own imports, with the resolver contract of the raw [`compile`](#compile). It is asked after the entry and before the SDK's own modules |
| `resolvePackage` | `ResolvePackage` | none | Maps a bare import specifier to a path `resolve` serves. It is asked after the SDK's own `@sauce/token` |
| `defines` | `Record<string, bigint \| boolean \| number>` | none | Compile-time constants. Each replaces a module-scope `const` of that name in every module, and each value is one 256-bit word. A program's inputs belong in `main`'s parameters, not here |
| `tokens` | `Readonly<Record<string, AddressInput>>` | none | Token symbol to address overrides, merged symbol by symbol over the chain's token registry. EVM chains only |
| `contracts` | `ContractsConfig` | none | ABI bindings by local name, `{ Name: { abi } }`, with the ABI parsed. A binding of the same name with a different ABI throws |
| `accessors` | `boolean \| ProtocolRewriteOptions` | `true` | Resolves protocol namespaces to the chain's addresses and rewrites a bare `pot` read to `ctx.self()`. `{ potSugar: false }` keeps the namespaces and turns off the `pot` rewrite; `false` turns both off. |
| `ambient` | `string[]` | none | Module specifiers whose exports every module can use with no `import`. A name the module declares or imports itself takes precedence |

### Returns

`CompiledSauceProgram`:

| Field | Type | Contents |
| - | - | - |
| `chain` | `CanonicalChain` | The chain record: `id`, `slug`, `name`, `kind`, `aliases` |
| `bytecode` | `Uint8Array` | The program. A cook runs it with the encoded entry arguments appended |
| `entryEncoding` | `"abi-v1"` | How `main`'s arguments are encoded. ABI-v1 is the only encoding |
| `entryParams` | `readonly EntryParam[]` | `main`'s run-time parameters in order, each `{ name, type }`. On Solana, without the `Account` parameters, which the manifest carries |
| `manifest` | `AccountManifest` | Solana only: the accounts to attach, in order. Absent on EVM chains |
| `derivedAccounts` | `Readonly<Record<string, string>>` | Solana only: the accounts the SDK added to `main` for token methods, by parameter name, with how each is derived: `pot:USDC` where the program names `pot`, `authority:USDC` where it does not, `@to:USDC` |
| `derivedValues` | `readonly DerivedValue[]` | Solana only: the `Uint256` values the SDK added after the accounts, which the cook's builder computes |
| `stoppable`, `stoppableRuns` | `readonly string[]`, `Readonly<Record<string, number \| null>>` | Solana only: the venue swaps at a price that a stop could hold, and how many times each runs in a loop |
| `potMention` | `SourcePosition` | Solana only: where the program first names `pot`, as a 1-based `{ line, column }`. A program that names it anywhere derives every account it acts with from the Pot, so a cook with no Pot (`--via engine`) refuses it. Absent where the program never names `pot` |
| `entrySignature` | `EntrySignature` | `main`'s signature as the compiler reports it: `params`, each `{ name, type, abi }`, including the parameters bound with `args` and Solana's `Account` parameters, and `returns`, the declared return type (`"void"` when `main` returns nothing). The `sauce` command decodes a cook's return data with it |
| `source` | `string` | The source after the SDK's token and protocol rewrites |

### Throws

* An unknown chain: `Unknown chain 'not-a-chain' (known slugs: ethereum, optimism, …)`.
* A use of a protocol name the chain lacks, outside a test of it: ``compileSauceRoute: `Aerodrome.Router` is not deployed on ethereum. A contract the chain lacks is zero there, and only a test of it may use it: `if (Aerodrome.Router) { … }` compiles the branch only where the chain has it. It is deployed on base.``
* A compile error, with source locations. See [Compile errors](#compile-errors).

### Bind arguments at compile time

`options.args` fixes `main`'s arguments in the bytecode. The result is a different program, specialised to those values, that takes no arguments at run time.

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

const source = `
function main(to: Address, amount: Uint256) {
  require(pot.USDC >= amount, "not enough USDC");
  USDC.transfer(to, amount);
}
`;

const reusable = routes.compileSauceProgram("base", source);
const bound = routes.compileSauceProgram("base", source, {
  args: ["0x2222222222222222222222222222222222222222", 1_000_000n],
});

console.log("reusable:", reusable.entryParams.length, "parameters,", reusable.bytecode.length, "bytes");
console.log("bound:", bound.entryParams.length, "parameters,", bound.bytecode.length, "bytes");
console.log(JSON.stringify(bound.entrySignature));
```

```text theme={null}
reusable: 2 parameters, 223 bytes
bound: 0 parameters, 245 bytes
{"params":[{"name":"to","type":"Address","abi":{"name":"to","type":"address"}},{"name":"amount","type":"Uint256","abi":{"name":"amount","type":"uint256"}}],"returns":"void"}
```

The bound program is not smaller: values fixed at compile time can make it larger. Its `entryParams` is empty, and its `entrySignature` still lists both parameters. To compile once and pass values per cook, leave `args` out and encode the values with [`routes.encodeEntryArguments`](https://docs.eco.com/sauce-sdk/typescript/entry-arguments).

A bound argument binds `main`'s parameter and nothing else. With `args: [5n]`, this program returned 30005 from a cook on a local fork of Base at block 52991146 with the current contracts: `fee()` read the module's `FEE`, 30, and `main` read its bound parameter, 5:

```ts theme={null}
const source = `
const FEE = 30;
function fee(): Uint256 { return FEE; }
function main(FEE: Uint256): Uint256 { return fee() * 1000 + FEE; }
`;
const program = routes.compileSauceProgram("base", source, { args: [5n] });
```

### Programs that import a library

`compileSauceProgram` resolves the [`@sauce/*` libraries](https://docs.eco.com/sauce-libraries/overview) with no option: `@sauce/router`, the `@sauce/token` it imports and the pool ABIs it binds. This compiles the [`@sauce/router` fallback swap](https://docs.eco.com/sauce-libraries/router#the-try-forms), `swap-fallback.sauce.ts`, for two chains:

```ts router-compile.mts theme={null}
import { readFileSync } from "node:fs";
import { routes } from "@eco-incorp/sauce";

const source = readFileSync("swap-fallback.sauce.ts", "utf8");

for (const chain of ["base", "ethereum"]) {
  // @sauce/router resolves with no option: the SDK serves it, @sauce/token and their ABIs.
  const program = routes.compileSauceProgram(chain, source);
  const params = program.entryParams.map((p) => `${p.name}: ${p.type}`).join(", ");
  console.log(`${chain}: ${program.bytecode.length} bytes; main(${params})`);
}
```

```text theme={null}
base: 1145 bytes; main(amountIn: Uint256, minOut: Uint256, recipient: Address)
ethereum: 1169 bytes; main(amountIn: Uint256, minOut: Uint256, recipient: Address)
```

The same source compiles on each chain to that chain's USDC, WETH and factories, so the two programs differ. `routes.compileSauceRoute` and the actions package resolve the libraries the same way; the raw `compile` resolves them only through `resolveSdkModule` and `resolveSdkPackage`, under "The SDK's modules" below.

## routes.compileSauceRoute

Compiles a program and builds the one cook call that runs it on a Pot, in a single step.

### Import

```ts theme={null}
import { routes } from "@eco-incorp/sauce";
```

### Usage

```ts compile-route.ts theme={null}
import { routes, v12PredictPot } from "@eco-incorp/sauce";
import { v12EngineAddress } from "@eco-incorp/sauce/deployments";
import { getAddress } from "viem";

const owner = getAddress("0x1111111111111111111111111111111111111111");

const source = `
function main(): Uint256 {
  return pot.USDC;
}
`;

const route = routes.compileSauceRoute("base", source, {
  pot: v12PredictPot(owner),
  engine: getAddress(v12EngineAddress()),
  value: 0n,
});

console.log("segments:", route.compiled.bytecode.length, "of", route.compiled.bytecode[0]!.length, "bytes");
console.log("warnings:", route.compiled.warnings);
console.log("calls:", route.calls.length, "to", route.calls[0]!.target);
console.log(route.source);
```

```text theme={null}
segments: 1 of 59 bytes
warnings: []
calls: 1 to 0x0000000000000000000000006c9175d272d49e6c8c139a3fe6bc6fdb422ed3fe
import { IERC20 } from "./artifacts/IERC20.json";

function main(): Uint256 {
  return IERC20.at(USDC).balanceOf(ctx.self());
}
```

`source` shows the SDK's rewrite: `pot.USDC` became a `balanceOf` call on the chain's USDC.

### Signature

```ts theme={null}
function compileSauceRoute(
  destination: ChainRef,
  sauce: string,
  execution: SauceEvmExecutionInput | SauceSvmExecutionInput,
  options?: SauceCompileOptions,
): CompiledSauceRoute;

interface SauceEvmExecutionInput {
  pot: AddressInput;
  engine: AddressInput;
  value?: bigint | number | string;
}

interface CompiledSauceRoute {
  readonly calls: readonly CallInput[];
  readonly compiled: CompileResult;
  readonly source: string;
}

// The route compile's own result type, not the raw compiler's CompileResult
interface CompileResult {
  bytecode: Uint8Array[];
  warnings: string[];
}
```

### Parameters

| Parameter | Type | Meaning |
| - | - | - |
| `destination` | `ChainRef` | The chain, as for `compileSauceProgram` |
| `sauce` | `string` | The program's source |
| `execution` | `SauceEvmExecutionInput` | On an EVM chain: `pot`, the Pot to cook on; `engine`, the engine the cook names; `value`, the cook's value (the Kitchen fee plus native coin the program spends), default `0` |
| `options` | `SauceCompileOptions` | Optional. The options of `compileSauceProgram` without `args` and `stops` |

On Solana, `execution` names a staged code account, and the call it builds is not one the released Solana engine accepts. Build Solana cooks with the Kitchen client; see [Solana](https://docs.eco.com/sauce-sdk/typescript/solana.md).

### Returns

`CompiledSauceRoute`:

* `calls`: one call, `{ to, target, data, value }`. `to` is the Pot's checksummed address, `target` the same Pot as a 32-byte, left-padded address, and `value` the `execution.value` you passed, as a `bigint`.
* `compiled.bytecode`: a `Uint8Array[]` holding one segment, the program.
* `compiled.warnings`: always an empty array. A failed compile throws.
* `source`: the source after the SDK's rewrites.

### How it differs from compileSauceProgram

* It takes no run-time arguments: `calls` carries the bytecode with nothing appended.
* Its result has no `entryParams`, `entryEncoding`, `isaRevision` or Solana `manifest`.
* It supplies no `chainId`: a program that reads `chainId` fails to compile with `` `chainId` is not a declared variable ``.
* It supplies no ambient `@sauce/token` functions. Import them in the program, or pass `ambient: ["@sauce/token"]`:

```ts compile-route-ambient.ts theme={null}
import { routes, v12PredictPot } from "@eco-incorp/sauce";
import { v12EngineAddress } from "@eco-incorp/sauce/deployments";
import { getAddress } from "viem";

const execution = {
  pot: v12PredictPot(getAddress("0x1111111111111111111111111111111111111111")),
  engine: getAddress(v12EngineAddress()),
};

// `balanceOf` is an @sauce/token function. compileSauceProgram supplies it with no import.
const source = `
function main(): Uint256 {
  return balanceOf(USDC, pot);
}
`;

console.log("compileSauceProgram:", routes.compileSauceProgram("base", source).bytecode.length, "bytes");

try {
  routes.compileSauceRoute("base", source, execution);
} catch (error) {
  if (!(error instanceof Error) || !("diagnostics" in error) || !Array.isArray(error.diagnostics)) throw error;
  const [first] = error.diagnostics;
  console.log("compileSauceRoute:", `${first.path}:${first.start.line}:${first.start.column}`, first.message);
}

const withAmbient = routes.compileSauceRoute("base", source, execution, { ambient: ["@sauce/token"] });
console.log("compileSauceRoute with ambient:", withAmbient.compiled.bytecode[0]!.length, "bytes");
```

```text theme={null}
compileSauceProgram: 70 bytes
compileSauceRoute: main.js:3:10 `balanceOf` is called but never declared
compileSauceRoute with ambient: 70 bytes
```

Use `compileSauceProgram` when you need `main`'s parameters or a Solana manifest.

## compile

The raw compiler. It resolves nothing: no chain names, no token syntax, no SDK modules unless your resolver serves them. Use it for full control over modules and compiler settings, for a closure entry, or for the reports of nested programs.

### Import

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

### Usage

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

const files: Record<string, string> = {
  "main.ts": `
function main(amounts: Uint256[], minimum: Uint256): Uint256 {
  let total = 0;
  for (let i = 0; i < amounts.length; i++) {
    total += amounts[i];
  }
  require(total >= minimum, "below minimum");
  return total;
}
`,
};

const artifact = compile({
  target: "evm",
  entry: "main.ts",
  resolve: (path) => (path in files ? new TextEncoder().encode(files[path]) : undefined),
});

console.log(artifact.bytecode.length, "bytes");
console.log(artifact.isaRevision, artifact.entryEncoding);
console.log(artifact.entryParams);
console.log(JSON.stringify(artifact.entrySignature));
console.log(artifact.programs, artifact.manifest);
```

```text theme={null}
246 bytes
stack-v3 abi-v1
[
  { name: 'amounts', type: 'uint256[]' },
  { name: 'minimum', type: 'uint256' }
]
{"params":[{"name":"amounts","type":"Uint256[]","abi":{"name":"amounts","type":"uint256[]"}},{"name":"minimum","type":"Uint256","abi":{"name":"minimum","type":"uint256"}}],"returns":"Uint256"}
[] undefined
```

Compiling is synchronous. The compiler fetches nothing itself: every module and JSON file comes through `resolve`.

### Signature

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

interface CompileOptions {
  target: SauceTarget; // "evm" | "svm"
  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;
}

type ResolveModule = (path: string) => Uint8Array | undefined | null;
type ResolvePackage = (specifier: string, importer: string) => string | undefined | null;

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: EntryParameter[];
  returns?: string;
}

interface EntryParameter {
  name: string;
  type: string;
  abi?: AbiParameter;
}

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

### Parameters

| Option | Type | Default | Meaning |
| - | - | - | - |
| `target` | `"evm" \| "svm"` | required | The engine the program is compiled for |
| `resolve` | `ResolveModule` | required | Returns a module's bytes for a path. Return `undefined` or `null` when nothing is at the path, so the compiler can try another candidate; throw when a file exists and cannot be read, which stops the compile |
| `resolvePackage` | `ResolvePackage` | none | Maps a bare specifier, such as `"@sauce/token"`, to a path `resolve` serves. `undefined` or `null` declines |
| `entry` | `string` | `"main.js"` | The entry module, the first path `resolve` is asked for |
| `entryKind` | `"module" \| "closure"` | `"module"` | How the entry's text is read: a SauceScript module, or one function expression as `Function.prototype.toString` prints it. See [Closure entries and bound arguments](#closure-entries-and-bound-arguments) |
| `contracts` | `Array<[name, path]>` | none | ABI bindings for the entry module, each an ABI file `resolve` serves |
| `inlineContracts` | `Array<[name, abi]>` | none | ABI bindings for the entry module, each ABI given as a JSON string |
| `ambient` | `Array<string>` | none | Modules whose exports every module can use with no `import`. A module's own names take precedence |
| `defines` | `Array<[name, value]>` | none | Compile-time constants as decimal or `0x` integer strings, each replacing a module-scope `const` of that name in every module. A program's inputs belong in `main`'s parameters |
| `args` | `Array<bigint \| boolean \| string>` | none | Values for `main`'s scalar parameters, in order, compiled in as known values. Bound parameters leave `entryParams` and stay in `entrySignature` |
| `optimize` | `boolean` | `true` | Optional optimization passes. `false` turns them off; every check still runs, and known values are still decided at compile time. It does not promise a larger or more literal program |
| `chain`, `chainProfiles` | `string`, `Record<string, ChainProfile>` | none | Compile against one chain's builtin profile. The [compiler package reference](https://docs.eco.com/sauce-sdk/compiler/api.md) describes profiles |
| `cache` | `boolean` | `true` | Reuse compiler work between compiles in one process. `false` forces a fresh compile |

### Returns

`CompileResult`:

* `bytecode`: the program, with no argument values in it.
* `isaRevision`: the instruction-set label, `"stack-v3"`. It is a label, not a compatibility check: a program runs on the engine release paired with the compiler version the SDK pins, which `V12_RELEASE_SETS` in `/deployments` records.
* `entryEncoding`: always `"abi-v1"`.
* `entryParams`: `main`'s run-time parameters as an ABI parameter list, in the JSON shape viem and Solidity use. A top-level tuple of fixed size carries `indirect: true`: `main` reads it behind an offset word. [Entry arguments](https://docs.eco.com/sauce-sdk/typescript/entry-arguments.md#raw-compiler-arguments) shows how to encode for it. On Solana, `Account` parameters are absent; the manifest lists them.
* `entrySignature`: `main`'s parameters in SauceScript's own type names (`Uint256`, `Int256`, `Address`, records written out), each with its `abi` entry, and `returns`, the declared return type. It keeps parameters bound through `args` and Solana `Account` parameters.
* `programs`: one `ProgramReport` per closure compiled as a program of its own with `program(closure, ...args)`, inner programs first.
* `manifest`: on Solana, the accounts `main` takes, by index and role. Absent on EVM.
* `chain`: the `chain` option echoed back, when one was given.

On Solana the manifest lists each `Account` parameter:

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

const source = new TextEncoder().encode(`
function main(destination: Account<Writable>, amount: Uint256): Uint256 {
  return amount;
}
`);

const artifact = compile({
  target: "svm",
  entry: "main.ts",
  resolve: (path) => (path === "main.ts" ? source : undefined),
});

console.log(artifact.entryEncoding, artifact.bytecode.length, "bytes");
console.log(JSON.stringify(artifact.manifest));
```

```text theme={null}
abi-v1 28 bytes
{"accounts":[{"kind":"slot","index":0,"name":"destination","requires":"writable"}]}
```

### Throws

A compile error, a resolve error or an invalid option. See [Compile errors](#compile-errors).

### Closure entries and bound arguments

With `entryKind: "closure"`, the entry is one arrow or function expression, exactly as `Function.prototype.toString` prints it. Its parameters and return type are `main`'s, and a concise body is `main`'s return. Diagnostics point into the closure text. `args` then compiles `main`'s scalar arguments in as known values, so the program takes none at run time:

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

// One function expression, as Function.prototype.toString prints it.
const closure = new TextEncoder().encode(
  "(amount: Uint256, minimum: Uint256): Uint256 => { require(amount >= minimum, \"below minimum\"); return amount; }",
);

const reusable = compile({ target: "evm", entryKind: "closure", resolve: () => closure });
const bound = compile({ target: "evm", entryKind: "closure", resolve: () => closure, args: [5_000_000n, 1_000_000n] });

console.log("reusable:", reusable.bytecode.length, "bytes;", reusable.entryParams.map((p) => `${p.name}: ${p.type}`));
console.log("bound:", bound.bytecode.length, "bytes;", bound.entryParams);
console.log(JSON.stringify(bound.entrySignature));
```

```text theme={null}
reusable: 66 bytes; [ 'amount: uint256', 'minimum: uint256' ]
bound: 18 bytes; []
{"params":[{"name":"amount","type":"Uint256","abi":{"name":"amount","type":"uint256"}},{"name":"minimum","type":"Uint256","abi":{"name":"minimum","type":"uint256"}}],"returns":"Uint256"}
```

The bound program decided `require(amount >= minimum)` while compiling. Its `entryParams` is empty, and `entrySignature` still lists both parameters. The closure is SauceScript text: the compiler does not run it as JavaScript or capture host variables.

Each `args` value is one word: a `bool`, or a non-negative integer below 2^256 as a `bigint` or a decimal or `0x` string. The raw option takes no bytes, arrays or records, and refuses `-10n` (`not a valid integer literal`); pass a negative `Int256` as its two's-complement word, `(1n << 256n) - 10n`. `compileSauceProgram`'s `args` takes `-10n` for an `Int256` parameter.

### Nested programs

A program can compile a closure as a program of its own with `program(closure, ...args)`. Each one appears in `programs`, with the names it captures from the program around it:

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

const source = new TextEncoder().encode(`
function main(fee: Uint256): bytes {
  const parcel = program((amount: Uint256): Uint256 => amount + fee, 7);
  return parcel.code.concat(parcel.args);
}
`);

const artifact = compile({
  target: "evm",
  entry: "main.ts",
  resolve: (path) => (path === "main.ts" ? source : undefined),
});

console.log("main:", artifact.bytecode.length, "bytes");
for (const { id, path, target, bytecode, captures } of artifact.programs) {
  console.log({ id, path, target, bytes: bytecode.length, captures });
}
```

```text theme={null}
main: 81 bytes
{ id: 0, path: [], target: 'evm', bytes: 50, captures: [ 'fee' ] }
```

`path` lists the programs a nested program sits in, outermost first, and `captures` lists the words its `args` carry, in order. Inside the program, `program()` returns `{ code, args }`; the captured values and the closure's parameters are read-only.

### The SDK's modules

`resolveSdkModule` and `resolveSdkPackage` serve, from memory, the files `compileSauceProgram` serves without being asked: the `@sauce/token` and `@sauce/router` libraries, `./artifacts/IERC20.json`, `./artifacts/ISauceRouter.json` and every `./protocols/<slug>/<Export>.json`. Fall back to them from your resolver:

```ts raw-compile-sdk-modules.ts theme={null}
import { compile, resolveSdkModule, resolveSdkPackage } from "@eco-incorp/sauce/compiler";

const entry = new TextEncoder().encode(`
import { balanceOf } from "@sauce/token";

function main(token: Address): Uint256 {
  return balanceOf(token, ctx.self());
}
`);

const artifact = compile({
  target: "evm",
  entry: "main.ts",
  resolve: (path) => (path === "main.ts" ? entry : resolveSdkModule(path)),
  resolvePackage: resolveSdkPackage,
});

console.log(artifact.bytecode.length, "bytes");
```

```text theme={null}
71 bytes
```

```ts theme={null}
function resolveSdkModule(path: string): Uint8Array | undefined;
function resolveSdkPackage(specifier: string): string | undefined;
```

The package also ships the libraries' files under `sdk/dist/std/token/` and `sdk/dist/std/router/` (one `.evm.js` and one `.svm.js` arm each, and the ABIs they bind) for a toolchain that resolves packages from disk. The compiler picks the arm by target.

## ready

Initializes the compiler's WebAssembly where the environment needs it. In Node it resolves at once and ignores `source`; in a browser, every compile throws until it has resolved.

### Import

```ts theme={null}
import { ready } from "@eco-incorp/sauce"; // also exported by "@eco-incorp/sauce/compiler"
```

### Usage

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

await ready();

const program = routes.compileSauceProgram("base", "function main(): Uint256 {\n  return pot.USDC;\n}\n");
console.log(program.bytecode.length, "bytes");
```

```text theme={null}
59 bytes
```

### Signature

```ts theme={null}
function ready(source?: WasmSource): Promise<void>;

type WasmSource = ArrayBuffer | ArrayBufferView | { href: string } | string;
```

### Parameters

| Parameter | Type | Meaning |
| - | - | - |
| `source` | `WasmSource` | Optional. Where the browser loads the wasm from: its bytes, a `URL`, or a URL string. With none, it loads `sauce_wasm_bg.wasm` from beside the compiler's module, through `new URL("sauce_wasm_bg.wasm", import.meta.url)`, which bundlers rewrite to an asset URL |

### Returns

A promise that resolves once the compiler can run. Calling `ready()` again is harmless, and a failed attempt is not remembered, so a later call tries again. The [overview](https://docs.eco.com/sauce-sdk/typescript/overview.md#in-a-browser) shows a browser entry that passes a URL.

## Compile errors

All three functions throw on a compile error. A located source error, such as the undeclared variable below, has three headlines with ANSI bold codes; the source line and caret are plain. An error from a failed import has more headlines, so do not rely on a fixed count. The codes are always emitted, so strip them (`stripVTControlCharacters` from `node:util`) before matching or logging the text. Its `diagnostics` array places each source error.

`compileSauceProgram` and `compileSauceRoute` can also throw a parse `SyntaxError` with no ANSI codes and no `diagnostics`. The raw `compile` reports a parse error through the compiler's error report instead.

```ts compile-error.ts theme={null}
import { stripVTControlCharacters } from "node:util";
import { compile } from "@eco-incorp/sauce/compiler";

const source = `function main(amount: Uint256): Uint256 {
  return amount + bonus;
}
`;

try {
  compile({
    target: "evm",
    entry: "main.ts",
    resolve: (path) => (path === "main.ts" ? new TextEncoder().encode(source) : undefined),
  });
} catch (error) {
  if (!(error instanceof Error)) throw error;
  console.log(stripVTControlCharacters(error.message));
  if ("diagnostics" in error && Array.isArray(error.diagnostics)) {
    for (const { path, message, start, end, code } of error.diagnostics) {
      console.log(`${path}:${start.line}:${start.column}-${end.line}:${end.column} ${code} ${message}`);
    }
  }
}
```

```text theme={null}
compiling the program failed
│
├─▶ compilation failed
│
╰─▶ `bonus` is not a declared variable
    ├╴`bonus` is not a declared variable (bytes 60..65)
    ╰╴error: `bonus` is not a declared variable
       --> main.ts:2:19
        |
      2 |   return amount + bonus;
        |                   ^^^^^
main.ts:2:19-2:24 UnknownIdentifier `bonus` is not a declared variable
```

Each diagnostic is `{ path, message, start, end, secondary?, code? }`:

* `path`: the module as `resolve` was asked for it. An error in an imported module names the import.
* `start`, `end`: `{ offset, line, column }`. Lines and columns start at 1, and counts are UTF-16 code units, so `source.slice(start.offset, end.offset)` is the span.
* `secondary`: a second location with its own note, where there is one.
* `code`: the error's stable name, such as `UnknownIdentifier`, `UnknownFunction` or `TypeMismatch`. Match on it rather than on `message`. A parse or subset error has none.

An error with no place in the source, such as an invalid option or a resolver failure, has an empty `diagnostics` array. A message your resolver throws is kept. `compileSauceProgram` and `compileSauceRoute` compile the rewritten source as `main.js`, so their diagnostics name `main.js`. The `/compiler` subpath does not export a type for the error; narrow it as the example does.

## Next steps

* [Entry arguments](https://docs.eco.com/sauce-sdk/typescript/entry-arguments.md): encode `main`'s arguments for a compiled program.
* [Cook transactions](https://docs.eco.com/sauce-sdk/typescript/cook-transactions.md): turn a compiled program into a transaction.
* [sauce compile](https://docs.eco.com/sauce-sdk/cli/compile.md): the same compile from a terminal.
* [Bytecode](https://docs.eco.com/sauce/bytecode.md): what the compiler emits.
* [@eco-incorp/sauce-compiler reference](https://docs.eco.com/sauce-sdk/compiler/api.md): the compiler package's API.


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