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

# Bytecode

> Compiling a program produces bytecode for one chain. Read this to know what a transaction carries, and to check that it runs the program you compiled.

Compiling a program produces bytecode for the [V12 engine](https://docs.eco.com/sauce/v12.md) on one chain. The compiler checks the program, resolves each name to its value on that chain and folds it into the bytes. The result is one reusable program: the values of `main`'s arguments are not in it, and are appended to it for each cook.

## What compiling produces

```ts pay.sauce.ts theme={null}
// Pays `amount` of the chain's USDC from the Pot to `to`.
//
//   sauce run pay.sauce.ts --chain base --wallet <name> --args <to> <amount>
//
//   to: Address       the recipient
//   amount: Uint256   in USDC's smallest unit (6 decimals): 5000000 is 5 USDC
function main(to: Address, amount: Uint256) {
  require(pot.USDC >= amount, "not enough USDC");
  USDC.transfer(to, amount);
}
```

```bash theme={null}
sauce compile pay.sauce.ts --chain base --json
```

```json theme={null}
{
  "chain": "base",
  "chainId": 8453,
  "target": "evm",
  "bytecode": "0x…",
  "size": 223,
  "payload": null,
  "entryEncoding": "abi-v1",
  "entryParams": [
    { "name": "to", "type": "Address" },
    { "name": "amount", "type": "Uint256" }
  ],
  "returnType": null,
  "defines": [
    { "name": "USDC", "value": "0x8335…2913", "source": "chain", "kind": "token", "chain": "base" }
  ],
  "contracts": [],
  "compiler": { "name": "@eco-incorp/sauce-compiler", "version": "…" },
  "engine": { "release": "v12-evm", "version": "…", "commit": "…", "address": "0x0F50…0f67" }
}
```

| Field | What it holds |
| - | - |
| `bytecode`, `size` | the program, and its length in bytes |
| `payload` | with `--args`: the size of the program with the arguments appended, and of the arguments alone |
| `entryEncoding` | how `main`'s arguments are encoded: always `abi-v1` |
| `entryParams` | `main`'s parameters, by name and type |
| `returnType` | the type `main` declares it returns; `null` when it returns nothing |
| `defines` | each name the chain gave the program, with its value |
| `contracts` | each protocol contract the program calls or reads, such as `AaveV3.Pool`, resolved on the chain |
| `compiler`, `engine` | the compiler that produced the bytes, and the engine release that runs them |
| `manifest` | Solana only: every account the program needs, in order, with its role |
| `cook` | Solana only: the size of the cook transaction, against the 1,232-byte limit |

The compiler package's own `compile()` returns `bytecode`, `isaRevision`, `entryEncoding`, `entryParams`, `entrySignature`, `programs`, the `chain` it was compiled for when one was named, and, on Solana, `manifest`:

* `entryParams` lists `main`'s parameters as ABI parameters. A top-level parameter that is a tuple of fixed size carries `indirect: true`: encode it as a dynamic parameter, behind an offset word. Encoded inline, the cook reverts with no data.
* `entrySignature` is `main`'s signature as the program declares it: each parameter's type in SauceScript's names, and the return type. A host encodes `main`'s arguments and decodes what it returns from it without reading the source.
* `programs` lists each closure the compile built as a separate program with `program(closure, ...args)`: its bytecode, the chain and target it was compiled for, and the values it captures.

The [compiler reference](https://docs.eco.com/sauce-sdk/compiler/api.md) documents each field.

## Names are part of the bytes

`USDC` above compiles to Base's USDC address, so the same source gives different bytes on another chain. `sauce inspect` reports a compiled program's size and keccak256:

```bash theme={null}
sauce inspect pay.sauce.ts --chain base --offline
sauce inspect pay.sauce.ts --chain ethereum --offline
```

```text theme={null}
pay.sauce.ts for base (evm)
  program: 223 bytes, keccak256 0x2de5…9846
  main(to: Address, amount: Uint256), arguments abi-v1
pay.sauce.ts for ethereum (evm)
  program: 223 bytes, keccak256 0x820b…8d82
  main(to: Address, amount: Uint256), arguments abi-v1
```

A literal is part of the bytes too. Compiled for Base, `USDC.transfer(to, 5000000)` and `USDC.transfer(to, 6000000)` give programs of the same size with different hashes (`0x33b1…2737` and `0x1cb2…7109`). A value that changes from one cook to the next belongs in a `main` parameter. `add.sauce.ts` below uses no chain-specific name, and compiles to the same 45 bytes, with the same hash, on Base and on Ethereum.

## Entry arguments

`main`'s arguments are encoded after the bytecode, in declaration order. Together they are the `program` argument of [`cook()`](https://docs.eco.com/sauce/cook). One compiled program serves every argument value, and its bytes and hash stay the same.

The encoding is ABI-v1, the only one: the values are ABI-encoded with no function selector, because entry arguments are not a contract call. Each parameter takes one 32-byte head word, so a record parameter sits behind an offset word even when all its fields have a fixed size; a fixed-size record inside an array or another record is written inline, as `abi.encode` writes it.

```ts add.sauce.ts theme={null}
// Returns a + b.
//
//   sauce compile add.sauce.ts --chain base --json --args <a> <b>
//
//   a: Uint256   the first number
//   b: Uint256   the second number
function main(a: Uint256, b: Uint256): Uint256 {
  return a + b;
}
```

Compiled for Base with `--args 7 35`, the program is 45 bytes and the arguments 64, two words: `payload` reports 109 bytes in all.

On Solana, `Account` parameters are not arguments: they are the accounts in the manifest, attached to the transaction. An `Address` argument on Solana is one 32-byte word, the whole public key. The encoding covers only what reaches `main`: the calls the program makes use each contract's own ABI.

## Size

On an EVM chain the program with its arguments is the `program` argument of the cook, in the transaction's calldata. `sauce compile --json` reports its size.

On Solana a transaction holds at most 1,232 bytes. `sauce compile --json` measures the cook transaction against that limit, as the Kitchen's cook or, with `--via engine`, the engine's direct one. For `pay.sauce.ts` (abridged):

```bash theme={null}
sauce compile pay.sauce.ts --chain solana --json --args <to> 5000000
```

```json theme={null}
"size": 695,
"payload": { "size": 759, "argumentsSize": 64 },
"manifest": {
  "accounts": [
    { "kind": "slot", "index": 0, "name": "potUSDC", "requires": "writable", "derive": "pot:USDC" },
    { "kind": "slot", "index": 1, "name": "pot", "requires": "readonly_signer", "derive": "pot" },
    { "kind": "slot", "index": 2, "name": "tokenProgram", "requires": "readonly", "derive": "Toke…Q5DA" },
    { "kind": "slot", "index": 3, "name": "toUSDC", "requires": "writable", "derive": "@to:USDC" }
  ]
},
"cook": { "via": "kitchen", "size": 1266, "limit": 1232, "fits": false }
```

Through the Kitchen, that cook is 1,266 bytes and does not fit. `--via engine` refuses this program, because it names `pot` and a direct cook on the engine has no Pot; a program that never names `pot` derives its accounts from `authority` (`authority:USDC`) and sizes either way. A cook that does not fit is run in one of two ways:

* **Staging.** The program is first written into a code buffer of the Kitchen, in several transactions, and the cook then runs it from the buffer. The Kitchen runs only a finalized buffer, and finalizing checks the SHA-256 digest of the bytes written. A cook directly on the engine carries the digest itself, and the engine runs exactly the bytes it hashed. After the cook the buffer is closed and its rent returned. In a simulation in the `sauce` command's local copy (LiteSVM) of the cluster at slot 460261665, `pay.sauce.ts` was staged in 3 transactions, and the buffer's rent was 5,618,480 lamports, returned when the buffer is closed.
* **Lookup tables.** Each account a transaction lists takes 32 bytes. An address lookup table is a list of addresses kept on chain: a transaction points into it and spends one byte per account. A table holds rent until its owner closes it.

`sauce run` stages a program and creates a lookup table when a cook needs them, and `sauce tx` prints the plan of transactions. A code buffer holds at most 1,048,576 bytes.

## Reproducible builds

The same source, compiled for the same chain by the same compiler, gives the same bytes. Compiling `pay.sauce.ts` for Base twice gives the keccak256 `0x2de5…9846` both times; compiling it for Ethereum gives `0x820b…8d82`.

A different compiler release can emit different bytes for an unchanged source. Anything that pins bytes, such as a stored program hash, a cached program or a program staged on Solana, has to be derived again when the compiler changes. To rebuild a program you keep, keep with it the compiler's release, the chain, the source and every module it imports, the ABIs, and the arguments. `sauce compile --json` reports the compiler in `compiler`.

The settlement program the SDK ships, which sweeps a Pot's tokens to one recipient with a minimum, shows it. The current compiler builds it into 410 bytes with the keccak256 `0x65e8…449b`, and that pair is the pin the SDK's verifier checks by default. A payload built by another compiler build carries its own hash and length. The verifier accepts it only when you pass that build's `{ hash, bytes }` pin explicitly, beside the current one, or `--pin <hash:bytes>` to [`sauce verify`](https://docs.eco.com/sauce-sdk/cli/verify).

## Checking what a cook runs

`sauce inspect` takes a cook apart. Given the cook's calldata and the source file, it recompiles the source for the chain, compares the program, and decodes the arguments against `main`:

```bash theme={null}
sauce inspect <cook calldata> --chain base --source pay.sauce.ts
```

```text theme={null}
Pot.cook on engine 0x0F50…0f67 (the released v12 engine …)
  program: 287 bytes, keccak256 0x4be1…8370
    pay.sauce.ts's program, 223 bytes, then 64 of arguments
      to: Address = 0x2222…2222
      amount: Uint256 = 5000000
```

The 287 bytes are the 223-byte program and two 32-byte arguments. When the program does not match, the line says so, here for the same cook checked against a compile for Ethereum:

```text theme={null}
    not pay.sauce.ts's program, as compiled for ethereum with these defines
```

The command exits 0 in both cases: read the line. A transaction hash with `--chain` is fetched first, so a sent cook can be checked the same way (`sauce inspect <tx hash> --chain base --source pay.sauce.ts`). Inspect reports a program by its size and hash; it prints no instruction listing.

## Next steps

* [cook()](https://docs.eco.com/sauce/cook.md): how the program and its arguments are sent and run.
* [The V12 engine](https://docs.eco.com/sauce/v12.md): what runs the bytecode, and how compiler and engine are paired.
* [`sauce compile`](https://docs.eco.com/sauce-sdk/cli/compile) and [`sauce inspect`](https://docs.eco.com/sauce-sdk/cli/inspect): every flag.


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