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

# Names

> How a program refers to tokens and contracts by name instead of by address, and what each name resolves to on the chain you compile for. Read it before you write a program.

A program refers to tokens, contracts and chains by name. The SDK resolves every name for the chain the program is compiled for, from the registries that ship in the package, before the compiler sees the program. Names are program syntax, not JavaScript values: a program is compiled, never run as TypeScript.

## How a name resolves

```ts resolve.sauce.ts theme={null}
// Pays `amount` of the chain's USDC from the Pot to `to`.
function main(to: Address, amount: Uint256) {
  require(pot.USDC >= amount, "not enough USDC");
  USDC.transfer(to, amount);
}
```

`sauce compile resolve.sauce.ts --chain base` writes Base's USDC address into the bytecode. Compiling the same file with `--chain ethereum` or `--chain solana` writes that chain's USDC. One file serves every chain whose names it uses, and each compile produces its own bytecode.

* **Resolution is per chain and happens at compile time.** Nothing of the name is left in the bytecode; the engine on chain only reads addresses.

* **`sauce compile --json` lists what was applied.** `defines` holds each token and `chainId` with its value, and `contracts` holds each namespace contract with its address (addresses abbreviated here):

  ```json theme={null}
  {
    "defines": [
      { "name": "USDC", "value": "0x8335…2913", "source": "chain", "kind": "token", "chain": "base" }
    ]
  }
  ```

* **A use of a name the chain lacks is a compile error at the use, naming the chain.** The error gives the file, line and column. Nothing is compiled, so nothing can be sent. This program compiles for Ethereum, which registers `USDT`:

  ```ts missing-token.sauce.ts theme={null}
  // Pays `amount` of the chain's USDT from the Pot to `to`.
  function main(to: Address, amount: Uint256) {
    USDT.transfer(to, amount);
  }
  ```

  Base does not register `USDT`, so `--chain base` fails like this at `USDT.transfer`:

  ```text theme={null}
  `USDT` is not a token on base. Did you mean `USDC`? A token the chain lacks is zero there, and only a test of it may use it: `if (USDT) { … }` compiles the branch only where the chain has it. It is a token on ethereum, optimism, solana. …
  hint: USDT is not a registered token on base (it has USDC, WETH, 67 stock tokens): take its address as a parameter of main …
  ```

* **A protocol contract the chain lacks is refused the same way**, with the chains that have it. This program compiles for Base:

  ```ts missing-contract.sauce.ts theme={null}
  // Returns the 0.05% Uniswap V3 pool of `tokenA` and `tokenB`, or 0 when there is none.
  function main(tokenA: Address, tokenB: Address): Uint256 {
    return UniswapV3.Factory.getPool(tokenA, tokenB, 500);
  }
  ```

  Compiled with `--chain unichain`, it fails:

  ```text theme={null}
  `UniswapV3.Factory` is not deployed on unichain. A contract the chain lacks is zero there, and only a test of it may use it: `if (UniswapV3.Factory) { … }` compiles the branch only where the chain has it. It is deployed on ethereum, optimism, bsc, polygon, monad, arc, base, arbitrum and 2 more.
  ```

  To use a name only on the chains that have it, test it: [Using a name only where the chain has it](#using-a-name-only-where-the-chain-has-it).

A registry row records where a contract was deployed. It does not show that the contract still has code, that its owner has not paused it, or that a pool has liquidity today. A compile proves a name is in the registry; `sauce simulate` proves a call works on the chain as it is now.

[Chains](https://docs.eco.com/sauce-sdk/reference/chains.md), [Protocols](https://docs.eco.com/sauce-sdk/reference/protocols.md) and [Tokens](https://docs.eco.com/sauce-sdk/reference/tokens.md) list every name per chain. `sauce globals --chain <chain>` prints the same registries for one chain, with `--tokens`, `--protocols`, `--protocol <Namespace>` for a namespace's methods, `--stocks`, and `--search <text>`. See [`sauce globals`](https://docs.eco.com/sauce-sdk/cli/globals).

## Token symbols

A token symbol is that chain's token address. It is a value (`balanceOf(USDC, account)`) and the receiver of the token methods (`USDC.transfer(…)`).

* **Symbols are case-sensitive.** `SPCXC` is not `SPCXc`.

* **Not every chain registers symbols.** Only 19 of the 46 registry chains register any: Ethereum, Optimism, Unichain, Polygon, Monad, Sei, Mantle, Arc, Base, Plasma, Arbitrum, Ink, Berachain and Solana, and the testnets Arc Testnet, Base Sepolia, Ethereum Sepolia, Solana Devnet and Solana Testnet. Among those, the symbols differ: Base has `USDC` and `WETH`, Ethereum adds `USDT`, Unichain has only `USDT0`. On a chain that does not register a symbol, take the address as a `main` parameter and use [`Token(address)`](#tokenaddress-and-bound-tokens).

* **Tokenized stocks are tokens.** Each issuer's own ticker is the symbol, with `.` written as `_` (`BRK.Bx` is `BRK_Bx`). They take the same methods, balances and swaps as any token:

  ```ts stock.sauce.ts theme={null}
  // Pays `amount` of Coinbase's tokenized SpaceX from the Pot to `to`.
  function main(to: Address, amount: Uint256) {
    require(pot.SPCXc >= amount, "not enough SPCXc");
    SPCXc.transfer(to, amount);
  }
  ```

  The chain's stocks are not in its `sauce globals` token list, which counts them. `sauce globals --chain base --stocks` lists them and `--search spacex` finds one by symbol, company or ticker. One company has one token per issuer, so the symbol depends on the chain and the issuer.

* **A wrong case is refused**, with the right one suggested. On Base, `SPCXC.transfer(to, amount)` fails with this hint; write `SPCXc`, as `stock.sauce.ts` does:

  ```text theme={null}
  hint: SPCXC is SPCXc on base: a token's symbol is case-sensitive
  ```

## Balances

`<holder>.<TOKEN>` reads a holder's balance of a token. It is `TOKEN.balanceOf(holder)` written short and compiles to the same bytes.

```ts balances.sauce.ts theme={null}
// Adds the USDC the Pot holds, the USDC `account` holds, the WETH held by the
// Uniswap V4 PoolManager, the ETH locked in the WETH contract and the Pot's ETH.
function main(account: Address): Uint256 {
  return pot.USDC + account.USDC + UniswapV4.PoolManager.WETH + WETH.balance + pot.balance;
}
```

* **`pot`** is the Pot, and `pot.USDC` is the Pot's USDC.
* **The native coin is `.balance`.** `pot.balance` is `ctx.selfBalance()`, and `account.balance` is `ctx.balance(account)`. On Solana `pot.balance` is the Pot's lamports. `.<SYMBOL>` reads only a token the chain registers.
* **A holder on an EVM chain** is any address: `pot` or `ctx.self()`, a variable or parameter, a protocol contract (`UniswapV4.PoolManager`), a contract at an address (`UniswapV3.Pool(pool)`), a call that returns an address, or a token contract (`WETH.balance` is the ETH locked in WETH).
* **A holder on Solana** must be known when the transaction is built, because its account is added to `main`: `pot`, `pot.owner`, an `Address` parameter of `main`, a token's mint, or a 32-byte key written out.
* **`.balance` on a value that is not an address reads a field**: a record, a list, a value `abi.decode` returns, or a struct a contract call returns. For the native coin of an address such a value holds, write `ctx.balance(x)`.

Two things are refused as a holder: a record or list, because a property named like a token is reserved for this form; and an interface with no address.

| Mistake | Compiler message | Write instead |
| - | - | - |
| A token-named field: `const r = { a: 1 }; return r.USDC;` | `` `r` is a record, not an address, and `USDC` names a token: `<holder>.USDC` reads a holder's USDC balance, so the name is reserved for that and cannot be a field. Name the field otherwise (`usdc`), or read the balance of an address it holds (`r.<field>.USDC`). `` | name the field otherwise (`usdc`), or read an address it holds (`r.<field>.USDC`) |
| An interface as the holder: `UniswapV3.Pool.WETH` | `` `UniswapV3.Pool` is an interface, with no address of its own: give it the address, `UniswapV3.Pool(<address>).WETH`. `` | `UniswapV3.Pool(pool).WETH`, with `pool` a `main` parameter |

`Token(address)` has no short form: its balance is `Token(address).balanceOf(holder)`.

### The Pot's owner and account names

`pot.owner` is the Pot's owner on both targets. On EVM it reads the Pot's `owner()`; a Shared Pot returns the zero address. On Solana it is the owner's account, which signs the cook. `pot.owner.USDC` and `pot.owner.balance` read the owner's token and native-coin balances.

On Solana, reading only `pot.owner` does not bind a Pot and permits `--via engine`. Use `Account(key)` for an account at a key, `Account.pda(program, [seeds])` for a PDA, and `Account.systemProgram`, `Account.tokenProgram`, `Account.token2022Program`, `Account.associatedTokenProgram` or `Account.memoProgram` for standard programs. These accounts are read-only unless given a role: `pot.owner.writable().signer()`.

An account's `.owner` is its owning program. To read the program that owns the Pot account, write `Account(pot.address).owner`.

A declaration or import of `pot` or `Account` shadows that global in its scope; a compile-time binding also takes precedence. `Account` in a type position remains the account type. The Pot's `owner`, `balance` and token-balance members are read-only; store values you need to change in your own locals. `ctx.self()`, `ctx.balance(x)` and `ctx.selfBalance()` remain valid.

### Arc: one balance, two scales

On Arc the native coin is USDC, and the ERC-20 `USDC` is the same balance at another scale: 18 decimals for the native coin, 6 for the token. `pot.USDC` reads the token and `pot.balance` the native coin:

```ts arc-balances.sauce.ts theme={null}
// Reverts unless the Pot holds at least `minWei` of the native coin (18 decimals)
// and at least `minUnits` of the ERC-20 USDC (6 decimals). Both read the same balance.
function main(minWei: Uint256, minUnits: Uint256) {
  require(pot.balance >= minWei, "native balance too low");
  require(pot.USDC >= minUnits, "token balance too low");
}
```

`pot.USDC` is the native balance divided by 10^12, rounded down, so less than 10^12 wei reads as 0. Convert one amount to the other's scale before comparing them. [EVM and Solana](https://docs.eco.com/sauce/targets.md#native-coins) lists the native coins.

## Token(address) and bound tokens

A token the registry does not carry is `Token(address)`, with the address as a `main` parameter:

```ts token-address.sauce.ts theme={null}
// Pays `amount` of `token` from the Pot to `to`.
function main(token: Address, to: Address, amount: Uint256) {
  const t = Token(token);
  require(t.balanceOf(pot) >= amount, "not enough tokens");
  t.transfer(to, amount);
}
```

A `Token(…)` can be the receiver of a call, or be bound once to a local that is only ever called:

* the binding is declared once with `const`, in the whole program;
* it is never reassigned;
* it is used only as `t.method(…)`.

Any other use is refused, because passing it on would reach the unchecked ABI write instead of the checked one. Passing `t` to a function, as in `show(t)`, fails with:

```text theme={null}
`t = Token(...)`: `t` is used other than as `t.<method>(...)`. `Token(addr)` is supported as the receiver of a call (`Token(addr).transfer(to, amount)`) or bound once to a local that is only ever called (`const t = Token(addr); t.approve(spender, amount)`). …
```

Pass the address instead, as `token-address.sauce.ts` passes `token`, and bind `Token(token)` where it is called.

On Solana, `Token(mint)` takes the mint as an `Address` argument. Its token program is read from the cluster when the cook is built, and a mint whose program cannot be read is refused. See [EVM and Solana](https://docs.eco.com/sauce/targets.md#token-programs).

## Token methods

| Method | Behaviour |
| - | - |
| `.transfer(to, amount)` | `transfer` with OpenZeppelin `SafeERC20` semantics |
| `.transferFrom(from, to, amount)` | `transferFrom` with `SafeERC20` semantics |
| `.approve(spender, amount)` | `forceApprove`: a non-zero allowance is reset to zero first |
| `.balanceOf(owner)` | a balance read; the long form of `owner.USDC` |

* A token that returns nothing, such as USDT, works when the address has code. A `false` return, a malformed return, or an address with no code reverts the program.

* A write returns nothing, so its result cannot be used. `const ok = USDC.transfer(to, amount);` fails with the message below; call the write as a statement, as `token-functions.sauce.ts` does:

  ```text theme={null}
  `USDC.transfer(...)` returns nothing, so its result cannot be used: a token write has SafeERC20 semantics and reverts the program unless the token accepted it. Call it as a statement.
  ```

* Approve the amount the next call spends and no more. An allowance that the program leaves standing outlives the program.

The methods compile to the [`@sauce/token`](https://docs.eco.com/sauce-libraries/token) functions of the same names, which lists every function and what each one checks.

### The same functions without a method

`@sauce/token`'s functions need no import. They take the token as the first argument:

```ts token-functions.sauce.ts theme={null}
// Reverts unless `spender` has no USDC allowance left from the Pot after the transfer.
function main(spender: Address, to: Address, amount: Uint256) {
  transfer(USDC, to, amount);
  require(allowance(USDC, pot, spender) == 0, "no allowance left standing");
  require(decimals(USDC) == 6, "unexpected decimals");
}
```

## Protocol namespaces

Every catalogued protocol has a namespace, in PascalCase (`uniswap-v3` is `UniswapV3`), resolved for the compiled-for chain with its addresses and ABIs from the registry. A member is one of three kinds:

| Member | Example | In a program |
| - | - | - |
| A deployed contract | `UniswapV3.Factory`, `AaveV3.Pool` | its address as a value; `.method(…)` calls it |
| An address only | `AaveV3.PoolAddressesProvider` | its address; the registry records no ABI |
| An interface | `UniswapV3.Pool`, `Erc4626.Vault` | no fixed address: called with an address, `UniswapV3.Pool(pool).liquidity()` |

```ts namespaces.sauce.ts theme={null}
// Reads a pool the program is given, and the pool the factory finds for WETH/USDC.
function main(pool: Address): Uint256 {
  const provider = AaveV3.PoolAddressesProvider;
  require(provider != 0, "no provider");
  const found = UniswapV3.Factory.getPool(WETH, USDC, 500);
  return UniswapV3.Pool(pool).liquidity() + found.liquidity();
}
```

* **An interface is called with an address** in the same expression: `UniswapV3.Pool(pool).slot0()`. `UniswapV3.Pool.at(pool)` is the same. A deployed contract's name is never called with an address; `.at(address)` reaches another deployment of its ABI.

* **A contract a call returns** is called directly where the registry records what the returned address is: `found.liquidity()` above, and `Erc4626.Vault(vault).asset().decimals()`. [Contracts and ABIs](https://docs.eco.com/sauce/contracts-and-abis.md) covers calls.

* **A namespace's default contract** is called by the namespace alone when the namespace has exactly one deployed contract with an ABI. `AaveV3.supply(…)` calls `AaveV3.Pool.supply(…)`. A namespace with several contracts, such as `UniswapV3`, needs the contract's name.

* **A family alias** groups a protocol's versions. `Uniswap` covers `UniswapV2`, `UniswapV3` and `UniswapV4`, and `Aave` covers `AaveV2`, `AaveV3` and `AaveV4`. An alias names a contract only when exactly one member has it on the chain.

  ```ts alias.sauce.ts theme={null}
  // The only Uniswap version with a UniversalRouter is V4.
  function main(): Uint256 {
    return Uniswap.UniversalRouter;
  }
  ```

  `Uniswap.Factory` exists in V2 and V3, so it is ambiguous. `return Uniswap.Factory;` fails with the message below; name the version, `UniswapV3.Factory`, as `namespaces.sauce.ts` does:

  ```text theme={null}
  `Uniswap.Factory` is ambiguous under Uniswap (uniswap-v2, uniswap-v3): write `UniswapV2.Factory` or `UniswapV3.Factory`
  ```

* **A chain-qualified contract** is another chain's address and nothing more. A chain name qualifies the namespace:

  ```ts qualified.sauce.ts theme={null}
  // Ethereum's UniversalRouter address, as a value, in a program compiled for Base.
  function main(): Address {
    return Ethereum.UniswapV4.UniversalRouter;
  }
  ```

  The program still runs on the chain it is compiled for. A call through the qualified name is a call to that address on this chain, where it is some other contract or none, and the compiler does not stop it. Pass a qualified name to something that acts on the other chain; never call it. A token is never qualified: the symbol is always the compiled-for chain's.

## Using a name only where the chain has it

Every registry name is declared on every chain: each protocol namespace with every contract it has on any chain, family aliases included, and each token symbol some chain registers. Where the chain the program is compiled for lacks a contract or a token, the name is zero. A test of the name is decided when the program compiles, and the branch the test rules out is not compiled, so one program can name a venue or a token that some of its chains lack:

```ts presence.sauce.ts theme={null}
// Finds a 0.05% pool where the chain has Uniswap V3, and pays in USDT where the chain has it, else in USDC.
function main(tokenA: Address, tokenB: Address, to: Address, amount: Uint256): Uint256 {
  let pool = 0;
  if (UniswapV3.Factory) {
    pool = UniswapV3.Factory.getPool(tokenA, tokenB, 500);
  }
  if (USDT) {
    USDT.transfer(to, amount);
  } else {
    USDC.transfer(to, amount);
  }
  return pool;
}
```

This compiles for Base, Ethereum and Solana. On Base it pays in USDC; on Solana, which has no Uniswap V3 factory, it compiles without the pool lookup.

* **A test** is the name in a condition: `if (X)`, `!X`, `X != 0`, `X == 0`, `X ? a : b`, `X && …` and `X || …`. `X` is a contract (`UniswapV3.Factory`, `Uniswap.UniversalRouter`, or one on a named chain, `Arbitrum.UniswapV3.Factory`) or a token symbol (`USDT`). A guard that returns, `if (!USDT) return 0;`, leaves out the rest of its block where `USDT` is absent.
* **Any other use** of a name the chain lacks is refused at the use, naming the chain: a call on it, passing it, returning it, or `const a = X`. A zero never reaches the chain as a contract address. `require(X != 0)` on a chain without `X` is refused too, since it fails whatever the program does.
* **A decision is made only from** registry names, `true` and `false`, `!`, `&&`, `||` and comparisons with `0`. `X > 0` is a use of `X`, not a test. A decision held in a variable is live code: with `const has = USDT != 0; if (has) { USDT.transfer(to, amount); }`, Base refuses the transfer. A helper function is checked as if it ran, so it tests the name itself.
* **Outside a condition**, `X && Y` and `X || Y` give `true` or `false`, and each side is a test: `const both = USDT && UniswapV3.Factory;` compiles on Base, where `USDT` is absent. On a contract they give a bool too: `const factory = UniswapV3.Factory; return factory || 0;` returns `true`, not the factory's address.
* **A name no chain has is an error on every chain**, tested or not, so a typo cannot drop a branch. `if (UniswapV9.Factory) { … }` fails on Base with "`UniswapV9` is not a token or protocol on base. Did you mean `UniswapV2` or `UniswapV3` or `UniswapV4`? …", and the same holds for a contract or method the registry does not have.
* **A program's own declaration wins.** A local, parameter or function named like a registry name is the program's in the block it is declared in, from the declaration on. A type-only import (`import type { USDT }`) declares nothing.
* **A program that declares its own function named `available` keeps it.** A registry name is tested by the name itself, never by a call.

The names are not a module. A program reads them with no import, and an import that reaches them is refused: `import { USDT } from "@sauce/chain"` fails with "`@sauce/chain` is the chain's names, which a program reads with no import: write `UniswapV3.Factory`, `USDC`, `Base.UniswapV3.Factory` as they are."

## Chain names and chainId

* **A chain name** (`Base`, `Ethereum`, `Arc`, `Solana`) qualifies another chain's contract address, as above. [Chains](https://docs.eco.com/sauce-sdk/reference/chains.md) lists each name.
* **`chainId`** is the id of the chain the program was compiled for. It is a constant folded into the bytecode.

```ts chain-id.sauce.ts theme={null}
// Reverts when these bytes run on a chain other than the one they were compiled for.
function main() {
  require(ctx.chainId() == chainId, "compiled for another chain");
}
```

`ctx.chainId()` reads the chain the program runs on. The two differ only when bytecode compiled for one chain is cooked on another.

## Solana venue namespaces

Solana has no protocol namespaces. Each swap venue is a namespace of its own: its protocol, then its program. `sauce globals --chain solana` lists them under Venues.

```ts orca.solana.sauce.ts theme={null}
// Swaps `amountIn` USDC for WSOL at the Orca whirlpool for the USDC/WSOL pair at tick spacing 4,
// and pays the WSOL to `to`.
function main(to: Address, amountIn: Uint256, priceLimit: Uint256, minOut: Uint256): Uint256 {
  const got = Orca.Whirlpool.swap(Orca.Whirlpool.pool(USDC, WSOL, 4), USDC, WSOL, amountIn, priceLimit, minOut);
  if (got > 0) {
    WSOL.transfer(to, got);
  }
  return got;
}
```

[EVM and Solana](https://docs.eco.com/sauce/targets.md) covers the accounts a Solana program uses, and [`@sauce/router` on Solana](https://docs.eco.com/sauce-libraries/router#on-solana) lists the venues.

## Next steps

> **[Contracts and ABIs](https://docs.eco.com/sauce/contracts-and-abis.md)**
>
> Call a namespace's contracts, pass structs, destructure results.

> **[@sauce libraries](https://docs.eco.com/sauce-libraries/overview.md)**
>
> `@sauce/token` and `@sauce/router`: every function and what it checks.

> **[EVM and Solana](https://docs.eco.com/sauce/targets.md)**
>
> Which names, builtins and accounts each target has.

> **[Tokens reference](https://docs.eco.com/sauce-sdk/reference/tokens.md)**
>
> Every token symbol per chain.


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