> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.getfoundry.sh/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

## Foundry on Tempo

Foundry ships Tempo support in the main `foundry-rs/foundry` release. You no longer need a separate Tempo fork or a special `foundryup` channel.

The Tempo guide is a Foundry workflow hub. It explains how Tempo capabilities show up in `forge`, `cast`, and `anvil`, and links to generated command references for exact CLI syntax. Protocol-level details live in the [Tempo developer docs](https://docs.tempo.xyz/docs) and the numbered [Tempo Improvement Proposals](https://docs.tempo.xyz/docs/protocol/tips).

Tempo T6 is the current hardfork for hardfork-specific behavior. Most projects should avoid pinning old Tempo hardforks unless they intentionally need historical semantics.

This first implementation keeps Tempo as a single guide page. The related [MPP-backed RPC endpoints](/guides/mpp) guide stays separate and is linked here where payment-gated RPC behavior matters.

## Setup and configuration

### Create a Tempo project

Start from the Tempo template when you want Tempo example contracts, tests, scripts, and config:

```bash
$ forge init --network tempo my-tempo-app
$ cd my-tempo-app
```

See [`forge init`](/reference/forge/init) for the generated command reference.

### Configure `foundry.toml`

The normal local configuration is `network = "tempo"`. It tells Forge and Anvil to use Tempo EVM semantics when they are not already inferring them from a live Tempo fork.

```toml [foundry.toml]
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
network = "tempo"

[rpc_endpoints]
tempo = "https://rpc.tempo.xyz/"
moderato = "https://rpc.moderato.tempo.xyz/"
```

The legacy `tempo = true` form is still accepted for back-compat, but new projects should use `network = "tempo"`.

Use the RPC aliases with `--rpc-url` and `--fork-url`:

```bash
$ forge test --fork-url moderato
$ forge script script/Deploy.s.sol --broadcast --rpc-url tempo
```

Use environment variables if you do not want to commit literal endpoint URLs:

```toml [foundry.toml]
[rpc_endpoints]
tempo = "${TEMPO_RPC_URL}"
moderato = "${TEMPO_MODERATO_RPC_URL}"
```

When you fork a live Tempo RPC, Foundry can usually infer Tempo network semantics from the forked chain ID, so the explicit `network` setting is mainly for local, non-forked execution. Keep it in project config when tests, scripts, or local Anvil runs need Tempo behavior without relying on a live RPC.

Hardfork pinning is advanced opt-in behavior. Use it only when you need to reproduce historical semantics or lock a regression test to a particular activation point:

```toml [foundry.toml]
[profile.historical]
network = "tempo"
hardfork = "tempo:T6"
```

Do not pin a historical hardfork for normal project setup. Live Tempo endpoints and current local development should follow the latest supported Tempo semantics.

### Local Anvil and forks

Run Anvil with Tempo semantics when you are not forking:

```bash
$ anvil --network tempo
```

When you fork a live Tempo RPC, Anvil can infer the network from the upstream chain:

```bash
$ anvil --fork-url $TEMPO_RPC_URL
```

### Set TIP-20 balances

In Tempo mode, use `anvil_dealTIP20` to set a TIP-20 token balance for an account. It writes the balance directly into native TIP-20 storage, so it works with precompile-backed TIP-20 balances that ERC-20 slot discovery cannot reach.

```text
anvil_dealTIP20(address, tokenAddress, balance)
```

| Parameter | Type | Description |
| --- | --- | --- |
| `address` | `address` | Account whose balance is set |
| `tokenAddress` | `address` | TIP-20 token deployed through the active Tempo token factory |
| `balance` | `uint256` | Absolute balance to write |

```bash
$ cast rpc anvil_dealTIP20 $RECIPIENT $TIP20_TOKEN 0x56BC75E2D63100000 \
    --rpc-url http://127.0.0.1:8545
```

The balance is set to the exact value given, not added to the existing balance. Calling it again with a smaller value lowers the balance. No transaction is created or mined, no transaction hash is returned, and the token total supply is not changed. Anvil verifies that `tokenAddress` is a deployed TIP-20 through the Tempo token factory before writing state. This method is available only when Anvil is running in Tempo mode and is intended for local development and testing.

On a Tempo node, `anvil_dealERC20` first checks whether the token is a deployed TIP-20. If it is, Anvil uses the same native direct-write path as `anvil_dealTIP20`; otherwise it falls back to the existing ERC-20 storage-slot discovery. This means `anvil_dealERC20` can be used uniformly for both standard ERC-20 and TIP-20 tokens on Tempo.

See [`anvil`](/reference/anvil/anvil) for the full reference. Do not add historical hardfork flags to normal Anvil examples; pinning old hardforks should be intentional.

## Tempo transactions

Tempo transaction options apply to [`cast send`](/reference/cast/send), [`cast mktx`](/reference/cast/mktx), [`forge create`](/reference/forge/create), and [`forge script`](/reference/forge/script). Native Tempo batching uses [`cast batch-mktx`](/reference/cast/batch-mktx) and [`cast batch-send`](/reference/cast/batch-send). This guide gives a capability map; use the generated references for exact syntax.

For protocol semantics, start with Tempo's [transaction specification](https://docs.tempo.xyz/docs/protocol/transactions/spec-tempo-transaction), [fee-token guide](https://docs.tempo.xyz/docs/guide/payments/pay-fees-in-any-stablecoin), [parallel transaction guide](https://docs.tempo.xyz/docs/guide/payments/send-parallel-transactions), and [fee sponsorship guide](https://docs.tempo.xyz/docs/guide/payments/sponsor-user-fees).

### Fee tokens

Use `--tempo.fee-token` when a transaction should pay gas in a TIP-20 token. The value can be an address, numeric TIP-20 token ID, or known token symbol:

```bash
$ cast send $CONTRACT_ADDRESS "increment()" \
    --rpc-url tempo \
    --tempo.fee-token $FEE_TOKEN \
    --private-key $PRIVATE_KEY
```

If the flag is omitted, Tempo transaction building follows the network's [default fee-token rules](https://docs.tempo.xyz/docs/protocol/fees/spec-fee/#fee-token-preferences). Use [`cast classify`](/reference/cast/classify) when you need to inspect a raw Tempo transaction's payment/general lane classification.

### Nonce lanes and expiring nonces

Tempo supports parallelizable nonces. Use `--tempo.nonce-key` directly, or give lane names to the team through `tempo.lanes.toml` and pass `--tempo.lane`:

```toml [tempo.lanes.toml]
deploy = 1
payments = 2
maintenance = 3
```

```bash
$ forge script script/Deploy.s.sol \
    --rpc-url tempo \
    --broadcast \
    --tempo.lane deploy \
    --private-key $PRIVATE_KEY
```

Use `--tempo.expiring-nonce` with `--tempo.valid-before` and `--tempo.valid-after` when a transaction should only be valid during a timestamp window. Use `--tempo.expires` for a relative validity window.

### Batch transactions

Use [`cast batch-mktx`](/reference/cast/batch-mktx) to build a signed or unsigned native Tempo batch transaction, and [`cast batch-send`](/reference/cast/batch-send) to publish one from call specs. `forge script --batch` broadcasts a script's remaining transactions as one native Tempo batch transaction.

### Sponsorship

Tempo sponsorship lets a fee payer pay gas for another account's transaction. The sender signs the transaction with a fee payer placeholder, leaving the two sponsor-owned fields open: the fee token and the fee payer signature. The sponsor fills them by signing the sponsor digest, which commits to the full transaction, so nonce, gas, and fee fields are pinned once the digest exists. Protocol details live in Tempo's [fee sponsorship guide](https://docs.tempo.xyz/docs/guide/payments/sponsor-user-fees).

Foundry supports:

* `--tempo.sponsor` for the sponsor address.
* `--tempo.sponsor-signer` for in-band sponsor signing.
* `--tempo.sponsor-sig` for a precomputed sponsor signature.
* `--sponsor-url` for a remote sponsor service.
* `--tempo.print-sponsor-hash` when the sponsor needs the hash to sign out of band.

For multi-transaction `forge script --broadcast` runs, prefer `--tempo.sponsor-signer` over a static `--tempo.sponsor-sig` so retries and later transactions can receive fresh sponsor signatures.

#### Remote sponsor services

With `--sponsor-url`, the sender-signed transaction is forwarded to a sponsor service via `eth_signRawTransaction`. The service selects the fee token it pays with, adds its fee payer signature, and returns the fully sponsored transaction, which is then submitted through the regular RPC; no local sponsor key is required. Hosted Tempo networks run such a service, built on the Accounts SDK [relay handler](https://docs.tempo.xyz/docs/server/relay-handler); the deployed worker is public at [`tempoxyz/tempo-apps`](https://github.com/tempoxyz/tempo-apps/tree/main/apps/fee-payer).

```bash
$ cast send $TOKEN "transfer(address,uint256)" $RECIPIENT 1000 \
    --rpc-url tempo \
    --sponsor-url https://sponsor.tempo.xyz \
    --private-key $PRIVATE_KEY
```

#### Anvil's built-in fee payer

In Tempo mode, Anvil serves the sponsor service contract itself, so sponsored flows work locally without hosting a separate service: [`eth_signRawTransaction`](/anvil/rpc-methods#eth_signrawtransaction) sponsor-signs a sender-signed Tempo transaction, and raw transactions submitted with the sponsorship placeholder are sponsored automatically on submission. Point `--sponsor-url` at the node itself:

```bash
$ anvil --network tempo
```

```bash
$ cast send $TOKEN "transfer(address,uint256)" $RECIPIENT 1000 \
    --rpc-url http://127.0.0.1:8545 \
    --sponsor-url http://127.0.0.1:8545 \
    --private-key $PRIVATE_KEY
```

The sponsor account defaults to the last dev account and pays with its stored fee token. Pass `--tempo.fee-payer` to Anvil to sponsor with a different unlocked account; the sponsor must not equal the transaction sender.

### Access-key and session signing

For transaction-layer keychain signing, pass `--tempo.access-key` with `--tempo.root-account`. Foundry signs on behalf of the root account using the access key. For wallet sessions, use `--tempo.session` to sign with a temporary session key from `$TEMPO_HOME/wallet/sessions.toml`.

Scripts also support interactive session creation with `--session`, `--session-root`, `--session-expires`, `--session-scope`, `--session-target`, `--session-selector`, and `--session-spend-limit`. For persistent session setup outside a script run, see [`cast wallet session`](/reference/cast/wallet/session), [`cast wallet session create`](/reference/cast/wallet/session/create), and [`cast wallet session revoke`](/reference/cast/wallet/session/revoke).

## Tokens and virtual addresses

Tempo TIP-20 tokens are ERC-20-compatible where ordinary ERC-20 calls apply, so the generated [`cast erc20-token`](/reference/cast/erc20-token) helpers remain useful for balances, allowances, transfers, mints, and burns.

Use [`cast tip20-token`](/reference/cast/tip20-token) for Tempo-specific TIP-20 workflows:

* [`cast tip20-token create`](/reference/cast/tip20-token/create) deploys a new TIP-20 token through the TIP20Factory.
* [`cast tip20-token logo-check`](/reference/cast/tip20-token/logo-check) validates a logo URI offline against Tempo constraints.
* [`cast tip20-token logo-set`](/reference/cast/tip20-token/logo-set) updates a token logo URI.
* [`cast tip20-token mine`](/reference/cast/tip20-token/mine) mines a TIP-1022 salt for virtual-address registration.

Use [`cast virtual-address`](/reference/cast/virtual-address) for vaddr workflows:

* [`cast virtual-address create`](/reference/cast/virtual-address/create) mines and registers a master, then prints derived virtual addresses.
* [`cast virtual-address resolve`](/reference/cast/virtual-address/resolve) resolves a virtual address to its registered master.
* [`cast virtual-address watch`](/reference/cast/virtual-address/watch) tails incoming TIP-20 transfers to a virtual address.

Foundry keeps the workflow details here and links to Tempo's [TIP-20 docs](https://docs.tempo.xyz/docs/protocol/tip20/overview), [virtual-address docs](https://docs.tempo.xyz/docs/protocol/tip20/virtual-addresses), and [TIP-1022](https://docs.tempo.xyz/docs/protocol/tips/tip-1022) for protocol semantics.

## Policies and receive policies

Tempo T6 adds reusable TIP-403 registry policies and account-level receive policies. Foundry exposes both as `cast` workflows.

Use [`cast tip403`](/reference/cast/tip403) to manage reusable policy IDs:

* [`cast tip403 create`](/reference/cast/tip403/create) creates a simple whitelist or blacklist policy.
* [`cast tip403 info`](/reference/cast/tip403/info) shows a policy's type and admin.
* [`cast tip403 check`](/reference/cast/tip403/check) checks whether an address is authorized by a policy.
* [`cast tip403 whitelist`](/reference/cast/tip403/whitelist) updates whitelist membership.
* [`cast tip403 blacklist`](/reference/cast/tip403/blacklist) updates blacklist membership.

Use [`cast receive-policy`](/reference/cast/receive-policy) to apply those policy IDs to an account's inbound TIP-20 flow:

* [`cast receive-policy get`](/reference/cast/receive-policy/get) reads an account's configured receive policy.
* [`cast receive-policy validate`](/reference/cast/receive-policy/validate) checks whether a transfer or mint would be credited or held.
* [`cast receive-policy set`](/reference/cast/receive-policy/set) sets sender and token policy IDs for the caller.
* [`cast receive-policy receipt decode`](/reference/cast/receive-policy/receipt/decode) decodes an ABI-encoded claim receipt.
* [`cast receive-policy receipt balance`](/reference/cast/receive-policy/receipt/balance) checks the held TIP-20 balance for a claim receipt.
* [`cast receive-policy claim`](/reference/cast/receive-policy/claim) claims held funds to a target selected by the caller.
* [`cast receive-policy receipt burn`](/reference/cast/receive-policy/receipt/burn) burns held funds when the token authorizes it.

A mined TIP-20 transfer is not always a credited transfer. If a receive policy blocks delivery, the transaction can still be mined while funds are held by `ReceivePolicyGuard`. The blocked path emits `TransferBlocked` with a claim receipt. Later, [`cast receive-policy claim`](/reference/cast/receive-policy/claim) asks the guard to resume or reroute delivery based on the claim target and on-chain rules; reroute is an outcome of claim target selection, not a separate command. Burn flows consume the receipt through [`cast receive-policy receipt burn`](/reference/cast/receive-policy/receipt/burn).

For support and protocol details, see the Tempo [TIP-403 overview](https://docs.tempo.xyz/docs/protocol/tip403/overview), [TIP-403 specification](https://docs.tempo.xyz/docs/protocol/tip403/spec), [receive-policy docs](https://docs.tempo.xyz/docs/protocol/tip403/receive-policies), [receive-policy guide](https://docs.tempo.xyz/docs/guide/payments/configure-receive-policies), and [TIP-1028](https://docs.tempo.xyz/docs/protocol/tips/tip-1028).

## Access keys, keychain, and sessions

Tempo accounts can authorize limited access keys, temporary sessions, and T6 admin access keys. Foundry separates interactive wallet login, on-chain keychain management, offline authorization payloads, and transaction-layer signing.

Use [`cast tempo login`](/reference/cast/tempo/login) for local key authorization against wallet.tempo. It persists keys in `$TEMPO_HOME/wallet/keys.toml` or `~/.tempo/wallet/keys.toml`, and it can print the browser URL with `--no-browser` when a browser cannot be opened.

Use browser login for ordinary wallet authorization. For offline payloads, admin-key payloads, or flows that should not depend on a browser, use [`cast key-authorization`](/reference/cast/key-authorization) and [`cast keychain authorize`](/reference/cast/keychain/authorize).

### Keychain management

Use [`cast keychain`](/reference/cast/keychain) for account keychain operations:

* [`cast keychain list`](/reference/cast/keychain/list) and [`cast keychain show`](/reference/cast/keychain/show) inspect local key registry state.
* [`cast keychain check`](/reference/cast/keychain/check), [`cast keychain inspect`](/reference/cast/keychain/inspect), and [`cast keychain doctor`](/reference/cast/keychain/doctor) diagnose local and on-chain access-key state.
* [`cast keychain authorize`](/reference/cast/keychain/authorize) and [`cast keychain revoke`](/reference/cast/keychain/revoke) provision or revoke keys on-chain.
* [`cast keychain rl`](/reference/cast/keychain/rl) and [`cast keychain ul`](/reference/cast/keychain/ul) read and update spending limits.
* [`cast keychain ss`](/reference/cast/keychain/ss), [`cast keychain rs`](/reference/cast/keychain/rs), and [`cast keychain policy`](/reference/cast/keychain/policy) manage call scopes and policy entries.
* [`cast keychain burn-witness`](/reference/cast/keychain/burn-witness) and [`cast keychain is-witness-burned`](/reference/cast/keychain/is-witness-burned) handle TIP-1053 witnesses.
* [`cast keychain is-admin`](/reference/cast/keychain/is-admin), [`cast keychain verify`](/reference/cast/keychain/verify), and [`cast keychain verify-admin`](/reference/cast/keychain/verify-admin) verify active access-key or root/admin-key signatures.

### Offline key authorization

Use [`cast key-authorization encode`](/reference/cast/key-authorization/encode), [`cast key-authorization sign`](/reference/cast/key-authorization/sign), and [`cast key-authorization inspect`](/reference/cast/key-authorization/inspect) when you need to build, sign, review, or transport an authorization payload outside the normal on-chain [`cast keychain authorize`](/reference/cast/keychain/authorize) path.

T6 `KeyAuthorization` adds `is_admin` and `account` fields. Admin access keys are key-management keys: they can authorize and revoke other keys, but they cannot carry expiry, spending limits, or call scopes. The `account` binding prevents an admin authorization from being replayed across accounts that share the same admin key.

For protocol context, see Tempo's [AccountKeychain docs](https://docs.tempo.xyz/docs/protocol/transactions/AccountKeychain), [TIP-1011](https://docs.tempo.xyz/docs/protocol/tips/tip-1011), and [TIP-1049](https://docs.tempo.xyz/docs/protocol/tips/tip-1049).

## Local development, testing, and debugging

### Forge tests and scripts

`network = "tempo"` makes Forge use Tempo EVM behavior for local tests and scripts. Forked tests and scripts can infer Tempo behavior from live Tempo RPCs through `--fork-url` and `--rpc-url`.

When contracts call Tempo's `SignatureVerifier`, use the generated cheatcode references instead of hand-rolled test bindings where they apply:

* [`signKeychain`](/reference/cheatcodes/sign-keychain) signs a digest as an encoded Tempo keychain signature for `verifyKeychain`.
* [`signKeychainAdmin`](/reference/cheatcodes/sign-keychain-admin) signs a digest for root/admin verification through `verifyKeychainAdmin`.
* [`expectKeychainVerified`](/reference/cheatcodes/expect-keychain-verified) asserts that code under test calls `SignatureVerifier.verifyKeychain`.
* [`expectKeychainAdminVerified`](/reference/cheatcodes/expect-keychain-admin-verified) asserts that code under test calls `SignatureVerifier.verifyKeychainAdmin`.

Tempo also has testing helpers for TIP-20 logo behavior and implicit approvals:

* [`setTip20LogoURI`](/reference/cheatcodes/set-tip20-logo-uri), also available as `setLogoURI`, sets a TIP-20 token logo URI directly in storage.
* [`expectTip20LogoURIUpdated`](/reference/cheatcodes/expect-tip20-logo-uri-updated), also available as `expectLogoURIUpdated`, asserts the TIP-20 logo update event.
* [`isImplicitlyApproved`](/reference/cheatcodes/is-implicitly-approved) checks whether a spender is implicitly approved on the active Tempo hardfork.
* [`assumeImplicitApproval`](/reference/cheatcodes/assume-implicit-approval) skips fuzz or invariant inputs unless a spender is implicitly approved.

Run the tests with [`forge test`](/reference/forge/test). Use [`forge script`](/reference/forge/script) for scripted local simulation and broadcast flows.

### Trace and log debugging

Foundry decodes current Tempo surfaces in traces and logs, including `ReceivePolicyGuard`, TIP-403 policy registry calls, account-keychain calls, `SignatureVerifier`, and T6 admin-key calls/events. Relevant decoded events include `TransferBlocked`, `ReceiptClaimed`, `ReceiptBurned`, and `ReceivePolicyUpdated`.

A practical held-transfer debugging path is:

1. Start from [`cast logs`](/reference/cast/logs), [`cast trace`](/reference/cast/trace), Forge verbosity traces, or Anvil output.
2. Find the decoded `TransferBlocked` event and copy the claim receipt.
3. Use [`cast receive-policy receipt decode`](/reference/cast/receive-policy/receipt/decode) to inspect the receipt.
4. Use [`cast receive-policy receipt balance`](/reference/cast/receive-policy/receipt/balance) to check held funds.
5. Use [`cast receive-policy claim`](/reference/cast/receive-policy/claim) to release or reroute according to the target and on-chain guard rules, or [`cast receive-policy receipt burn`](/reference/cast/receive-policy/receipt/burn) when the token authorizes burning.

This workflow is important for support tooling: a mined transaction can represent held funds, so a receipt alone does not prove the recipient balance was credited.

## Reference map

Use this map to jump from workflow prose to generated command output:

* Setup and local execution: [`forge init`](/reference/forge/init), [`forge test`](/reference/forge/test), [`forge create`](/reference/forge/create), [`forge script`](/reference/forge/script), [`anvil`](/reference/anvil/anvil).
* Tempo transactions: [`cast send`](/reference/cast/send), [`cast mktx`](/reference/cast/mktx), [`cast batch-mktx`](/reference/cast/batch-mktx), [`cast batch-send`](/reference/cast/batch-send), [`cast classify`](/reference/cast/classify), [`cast channel-id`](/reference/cast/channel-id).
* Tokens and virtual addresses: [`cast erc20-token`](/reference/cast/erc20-token), [`cast tip20-token`](/reference/cast/tip20-token), [`cast tip20-token create`](/reference/cast/tip20-token/create), [`cast tip20-token logo-check`](/reference/cast/tip20-token/logo-check), [`cast tip20-token logo-set`](/reference/cast/tip20-token/logo-set), [`cast virtual-address`](/reference/cast/virtual-address).
* Policies and held transfers: [`cast tip403`](/reference/cast/tip403), [`cast receive-policy`](/reference/cast/receive-policy).
* Accounts and signing: [`cast tempo login`](/reference/cast/tempo/login), [`cast keychain`](/reference/cast/keychain), [`cast key-authorization`](/reference/cast/key-authorization), [`cast wallet session`](/reference/cast/wallet/session), [`cast wallet session create`](/reference/cast/wallet/session/create), [`cast wallet session revoke`](/reference/cast/wallet/session/revoke).
* Debugging: [`cast logs`](/reference/cast/logs), [`cast trace`](/reference/cast/trace), [`cast receive-policy receipt decode`](/reference/cast/receive-policy/receipt/decode), [`cast receive-policy claim`](/reference/cast/receive-policy/claim).
* Payment-gated RPCs: [MPP-backed RPC endpoints](/guides/mpp).

Use generated references for flag-level syntax and this guide for how the pieces fit together.
