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

# @sauce/token

> @sauce/token reads and moves tokens on EVM chains and Solana. Use this reference to check what each function takes and what it checks.

`@sauce/token` reads and moves tokens: ERC-20 on EVM chains, SPL Token and Token-2022 on Solana. The token methods (`USDC.transfer(…)`, `Token(token).approve(…)`, `pot.USDC`) compile to calls into it, so a token write has one implementation however it is spelled.

The CLI and `routes.compileSauceProgram` make these functions available without imports. Pass the token as the first argument. You can also import the functions explicitly:

```ts token-functions.sauce.ts theme={null}
// Pays `amount` of `token` from the Pot to `to` and returns `to`'s new balance.
function main(token: Address, to: Address, amount: Uint256): Uint256 {
  transfer(token, to, amount);
  return balanceOf(token, to);
}
```

```ts token-import.sauce.ts theme={null}
import { balanceOf, transfer } from "@sauce/token";

// The same program with the functions imported by name.
function main(token: Address, to: Address, amount: Uint256): Uint256 {
  transfer(token, to, amount);
  return balanceOf(token, to);
}
```

Using a function that your target does not support produces a compile error. For example, `transferChecked` is available only on Solana. On EVM, importing it reports "`transferChecked` is not exported by module …"; using it without an import reports "`transferChecked` is called but never declared". See [library target selection](https://docs.eco.com/sauce-libraries/overview.md#evm-and-solana-implementations).

## On EVM chains

| Function | Arguments | Returns | Kind |
| - | - | - | - |
| `balanceOf` | `token: Address, account: Account` | `Uint256` | read |
| `totalSupply` | `token: Address` | `Uint256` | read |
| `decimals` | `token: Address` | `Uint256` | read |
| `allowance` | `token: Address, owner: Address, spender: Address` | `Uint256` | read |
| `transfer` | `token: Address, to: Address, amount: Uint256` | nothing | checked write |
| `transferFrom` | `token: Address, from: Address, to: Address, amount: Uint256` | nothing | checked write |
| `approve` | `token: Address, spender: Address, amount: Uint256` | nothing | checked write |
| `permit` | `token: Address, owner: Address, spender: Address, value: Uint256, deadline: Uint256, v: Uint256, r: Uint256, s: Uint256` | nothing | allowance on a signature |
| `permitTransferFrom` | `token: Address, owner: Address, amount: Uint256, nonce: Uint256, deadline: Uint256, signature: bytes, permit2: Address` | nothing | pull on a signature |

### Reads

The reads are typed ERC-20 calls. A call to an address with no code returns no data, so the read reverts.

### Writes

The writes have OpenZeppelin `SafeERC20` semantics:

* **Empty return data is success only from an address with code.** A token that returns nothing, such as USDT, works. A write to an address with no code reverts, so a mistyped address cannot pass silently.
* **A non-empty reply must contain at least 32 bytes, with the first word equal to 1.** A `false` return, a shorter reply or a first word other than 1 fails the check.
* **`approve` is `forceApprove`.** It reads the Pot's current allowance for the spender, and when that is not zero, sets it to zero before setting the new amount. USDT refuses to change one non-zero allowance to another, so an approve works only from zero.
* **Writes return nothing.** A write either succeeds or raises a failure. An unhandled failure reverts the cook; EVM programs can use the [documented catch boundaries](https://docs.eco.com/sauce/errors.md) to handle one.
* **`transferFrom` spends an allowance.** `from` must have approved the Pot for at least `amount`.

Every token write the package offers has these semantics. The one unchecked spelling is a raw `IERC20.at(token).transfer(…)` binding that a program writes itself, which decodes the declared `bool`: it reverts on USDT and accepts `false`. See [Contracts and ABIs](https://docs.eco.com/sauce/contracts-and-abis.md).

### Signatures: `permit` and `permitTransferFrom`

`permit` sets an allowance on the holder's signature and moves nothing; a later `transferFrom` pulls. `permitTransferFrom` pulls into the Pot in one step:

* `permit` submits an EIP-2612 `permit(owner, spender, value, deadline, v, r, s)` and then requires the token's nonce for `owner` to have moved by exactly one. A token with no `permit` but a permissive fallback, such as WETH9, would otherwise pass for one that granted the allowance. A `transferFrom` then spends the allowance. The token must have `nonces(address)`, or the nonce read reverts.
* `permitTransferFrom` calls Permit2's signature transfer, moving `amount` from `owner` to the Pot on `owner`'s one-time signature. Permit2 spends `nonce`, rejects an expired `deadline`, and moves the tokens with the same empty-or-`true` check.

```ts pull-permit2.sauce.ts theme={null}
// Pulls `amount` USDC from `owner` into the Pot on a Permit2 signature, then pays it to `to`.
function main(owner: Address, amount: Uint256, nonce: Uint256, deadline: Uint256, signature: bytes, to: Address) {
  permitTransferFrom(USDC, owner, amount, nonce, deadline, signature, Permit2.Permit2);
  USDC.transfer(to, amount);
}
```

`sauce run --fund` signs a Permit2 or EIP-2612 permit with your wallet and adds these pulls to the program. See [Funding](https://docs.eco.com/sauce-sdk/cli/funding.md).

## On Solana

The Solana implementation supports both SPL Token and Token-2022. Its functions take the accounts they read or move through, because a Solana instruction lists every account it touches:

| Function | Arguments | Returns | Kind |
| - | - | - | - |
| `balanceOf` | `token: Address, account: Account` | `Uint256` | read |
| `ownerOf` | `token: Address, account: Account` | `Address` | read |
| `totalSupply` | `token: Account` | `Uint256` | read |
| `decimals` | `token: Account` | `Uint256` | read |
| `allowance` | `token: Address, account: Account, spender: Address` | `Uint256` | read |
| `transfer` | `token: Address, tokenProgram: Account, source: Account, dest: Account, authority: Account, amount: Uint256` | nothing | write, SPL Token only |
| `transferFrom` | the arguments of `transfer` | nothing | write, SPL Token only |
| `approve` | `token: Address, tokenProgram: Account, source: Account, delegate: Account, authority: Account, amount: Uint256` | nothing | write, SPL Token only |
| `transferChecked` | `token: Address, tokenProgram: Account, source: Account, mint: Account, dest: Account, authority: Account, amount: Uint256, decimals: Uint256` | nothing | checked write, either program |
| `approveChecked` | `token: Address, tokenProgram: Account, source: Account, mint: Account, delegate: Account, authority: Account, amount: Uint256, decimals: Uint256` | nothing | checked write, either program |

### Reads

* **A token account** must be owned by SPL Token, or by Token-2022 with the base account layout or an account-type byte of 2. A mint, a multisig or any other account is refused with `not a token account`, and an account of another mint with `wrong mint`.
* **`totalSupply` and `decimals`** take the mint as an `Account`, not an `Address`. The account must be an initialized mint of either program, or the read fails with `not a mint` or `uninitialized mint`.
* **`ownerOf`** returns the holder recorded in the token account, not the authority a write is signed by. SPL accepts the account's delegate as the authority too, so a program that measures its own balance requires `ownerOf(token, account)` to equal the authority first.
* **`allowance`** reads the account's single delegate. It returns the delegated amount when the account has a delegate and the delegate is `spender`, and 0 otherwise. A revoked delegate reads as 0.

### Writes

* **`transfer`, `transferFrom` and `approve`** call SPL Token's plain instructions. They require `tokenProgram` to be SPL Token (`not the token program`), `source` to be an SPL Token account (`not a token account`) of `token` (`wrong mint`), and `amount` to fit 64 bits (`amount over u64`). A Token-2022 callee or account is refused. `transferFrom` is `transfer`: SPL Token has one transfer instruction, and the authority you attach decides whether the owner or a delegate moves the funds.
* **`transferChecked` and `approveChecked`** carry the mint and its decimals. They require the callee to be SPL Token or Token-2022, the source, destination (for a transfer) and mint to be owned by that program, `mint` to be the named token, and `decimals` to be the mint's own (`wrong decimals`).
* **A Token-2022 mint whose transfer hook names a program is refused** with `transfer hook`, before anything moves. The hook's extra accounts cannot be carried in a fixed account list. A hook extension that names no program is no hook. An approval runs no hook, so `approveChecked` does not check for one.
* **The other Token-2022 extensions are the token program's to apply.** A transfer fee is withheld from what the destination receives, so measure the balance that arrived. A paused mint or a frozen account refuses the transfer with the token program's own error, and amounts are always raw base units.
* **`approve` sets one delegate.** SPL Token tracks a single delegate per account, so a second approve replaces the first, where an ERC-20 keeps an allowance per spender.
* **Amounts are 64-bit.** An approval of `2^256 - 1`, which an ERC-20 accepts, reverts with `amount over u64`.

### The token methods choose the write

The token methods pick the write by the mint's token program. An SPL Token mint (`USDC`, `USDT`, `WSOL`) moves with the plain `transfer`. A Token-2022 mint (`PYUSD` and the tokenized stocks) moves with `transferChecked`, using the registry's decimals. Compiling adds each account the call moves through to `main` and derives it:

```ts pay-pyusd.solana.sauce.ts theme={null}
// Pays `amount` PYUSD (a Token-2022 mint) from the Pot to `to`, and returns what arrived.
function main(to: Address, amount: Uint256): Uint256 {
  const before = to.PYUSD;
  PYUSD.transfer(to, amount);
  return to.PYUSD - before;
}
```

Called directly, the functions take accounts as `Account` parameters of `main`:

```ts checked.solana.sauce.ts theme={null}
// Moves `amount` of `mint` from `source` to `destination` with the checked transfer, and
// returns what `destination` received.
function main(
  tokenProgram: Account,
  source: Account<Writable>,
  mintAccount: Account,
  destination: Account<Writable>,
  authority: Account<Signer>,
  mint: Address,
  amount: Uint256
): Uint256 {
  const before = balanceOf(mint, destination);
  transferChecked(mint, tokenProgram, source, mintAccount, destination, authority, amount, decimals(mintAccount));
  return balanceOf(mint, destination) - before;
}
```

## Next steps

* [@sauce libraries](https://docs.eco.com/sauce-libraries/overview.md): importing, and the EVM and Solana implementations.
* [@sauce/router](https://docs.eco.com/sauce-libraries/router.md): swaps at each venue, built on these writes.
* [Names](https://docs.eco.com/sauce/names.md): token symbols, `pot.<TOKEN>` and the token methods.
* [EVM and Solana](https://docs.eco.com/sauce/targets.md): accounts, token programs and size limits on Solana.
* [`token` namespace](https://docs.eco.com/sauce-sdk/api/token/api): the TypeScript API that emits these calls for a host program.


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