Back to Blog

Foundry Cast Tutorial: Interact with Smart Contracts from the CLI (2026)

TL;DR: Cast is the Foundry CLI tool for interacting with EVM chains directly from the terminal — no frontend, no SDK, no boilerplate. It covers number/unit conversions, reading contract state with cast call, sending state-changing transactions with cast send, querying on-chain data (ETH balances, blocks, transactions), decoding calldata and event topics, estimating gas, and managing private keys securely via an encrypted keystore. RPC endpoints can be set as CLI flags, environment variables, or in foundry.toml.

Table of Contents


Foundry is one of the most widely used smart contract development frameworks in the Ethereum ecosystem. The full suite consists of three tools: Forge (compile, test, deploy), Anvil (local node), and Cast (CLI interaction). This post focuses on Cast.

Cast is a zero-dependency command-line Swiss Army knife for EVM chains. It replaces what would otherwise require a custom script in ethers.js or viem — things like checking an on-chain value, decoding a failed transaction's calldata, computing a function selector, or sending a one-off transaction — with a single terminal command. Cast works against any EVM-compatible network: Ethereum mainnet, L2s (Arbitrum, Optimism, Base, Polygon), testnets, and local Anvil nodes.

In this post we cover the most useful Cast commands with practical examples drawn from real Solidity development workflows.

Cast examples


In this section, we will see some practical examples of how to use Cast for different purposes, such as number conversion, reading smart contract values, writing smart contract values, and handling private keys.

If you want to copy the commands, just click on the command box and it will be copied to your clipboard.



Number and unit conversions


Cast has a rich set of conversion utilities. These are especially useful when reading raw bytes32 storage slots, working with token amounts that have 18 decimals, or computing hash values.

Decimal ↔ hexadecimal

cast to-dec 0x000000000000000000000000000000000000000000000000000000000000002a

cast to-hex 50

Token amount conversions (parse-units / format-units)

When calling an ERC-20 transfer or mint function you almost always work with wei-denominated amounts internally. cast parse-units converts human-readable amounts and cast format-units does the reverse:

cast parse-units 1.5 18
cast format-units 1500000000000000000 18

The second argument is the number of decimals (18 for most ERC-20 tokens, 6 for USDC).

Ether unit shortcuts

cast to-wei 1 ether
cast from-wei 1000000000000000000

Hash a value with keccak256 — useful for computing function selectors, TypeHash constants, or storage slot keys by hand:

cast keccak "Transfer(address,address,uint256)"

Type conversions

cast to-bytes32 0x1234
cast --to-checksum-address 0xd8da6bf26964af9d7eed9e03e53415d37aa96045

Those are just a sample — Cast also supports to-ascii, to-utf8, to-rlp, from-rlp, to-base, and more.

ABI utilities: signatures, calldata, and event topics


One of Cast's most powerful but underused feature sets is its ABI tooling. These commands let you work with raw transaction calldata, compute function selectors, and decode events \u2014 without needing a full dev environment.

Compute a function selector (first 4 bytes of keccak256)

cast sig "transfer(address,uint256)"

Output: 0xa9059cbb — this is the well-known ERC-20 transfer selector.

Reverse-lookup a selector in the 4byte.directory database

When you see an unknown 0xabcd1234 in a transaction and want to know what function it is:

cast 4byte 0xa9059cbb

Decode raw calldata — invaluable when debugging a reverted transaction:

cast decode-calldata "transfer(address,uint256)" 0xa9059cbb000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa960450000000000000000000000000000000000000000000000000de0b6b3a7640000

Compute an event topic (keccak256 of the event signature)

cast sig-event "Transfer(address indexed,address indexed,uint256)"

Output: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef — the ERC-20 Transfer topic.

Decode ABI-encoded return data

If you captured raw return data from a cast call and want to decode it manually:

cast abi-decode "balanceOf(address)(uint256)" 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000

Query blockchain state: balance, block, and transaction


Cast can query any live or archived on-chain state. These commands are perfect for quick checks during development or incident response without having to open a block explorer.

Check an ETH balance

cast balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --rpc-url $RPC_URL

Add --ether to get the result formatted in ETH instead of wei:

cast balance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 --ether --rpc-url $RPC_URL

Inspect the latest block

cast block latest --rpc-url $RPC_URL

Fetch a specific field only (e.g., timestamp, gasLimit, baseFeePerGas):

cast block latest --field baseFeePerGas --rpc-url $RPC_URL

Inspect a transaction

cast tx 0xabc123...txhash --rpc-url $RPC_URL

Read a raw storage slot — useful for verifying packed storage layouts or inspecting proxy implementation slots:

cast storage [contract_address] 0 --rpc-url $RPC_URL

The EIP-1967 implementation slot for a transparent proxy is a well-known value you can query directly:

cast storage [proxy_address] 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc --rpc-url $RPC_URL

Get the bytecode of a deployed contract

cast code [contract_address] --rpc-url $RPC_URL

Call smart contracts to read values


One of the most common use cases of Cast is to call smart contract functions to read data from the blockchain. It allows easily querying contract state without needing to write any code.

The aspects that must be considered are:

  • The contract address: The address of the smart contract you want to interact with.
  • The function signature: The signature of the function you want to call.
  • The network RPC URL: The URL of the Ethereum node you want to connect to. You can include it directly in the cast command, read it from a .env file, or configure it in the foundry.toml file.
    cast call [contract_address] "number()" --rpc-url [network_rpc_url]

In this second example, we are calling the allowance function of an ERC20 token contract to check the allowance that an address has given to another address. The main difference with the previous example is that this function requires two parameters: the owner address and the spender address. Furthermore, in this case we are passing the response type (uint256).

cast call [contract_address] "allowance(address, address)(uint256)" [param1] [param2] --rpc-url [network_rpc_url]

Call at a specific block number — useful for historical state queries, e.g., checking a balance as it was at block 20000000:

cast call [contract_address] "balanceOf(address)(uint256)" [address] --rpc-url $RPC_URL --block 20000000

Fetch multiple values at once using shell command chaining — no custom script required:

cast call [token_address] "name()(string)" --rpc-url $RPC_URL ; cast call [token_address] "symbol()(string)" --rpc-url $RPC_URL

Write smart contracts to update values


Another important use case of Cast is to send transactions to smart contracts to update their state. This allows you to interact with contracts and modify their data.

When sending transactions, you need to consider:

  • The private key: The private key of the account you want to use to sign the transaction.
  • The contract address: The address of the smart contract you want to interact with.
  • The function signature: The signature of the function you want to call.
  • The network RPC URL: The URL of the Ethereum node you want to connect to. You can include it directly in the cast command, read it from a .env file, or configure it in the foundry.toml file.

In this example, we are calling the mint function of an ERC20 token contract to mint new tokens to a specific address. The function requires two parameters: the recipient address and the amount of tokens to mint.

cast send --private-key $PRIVATE_KEY [contract_address] "mint(address,uint256)" [param1] [param2] --rpc-url $RPC_URL

In this second example, we are using the cast parse-units command to convert a human-readable token amount (5 tokens) into its corresponding smallest unit (wei for Ether or the token's smallest denomination). This is particularly useful when dealing with ERC20 tokens that have decimals.

cast send --private-key $PRIVATE_KEY [contract_address] "mint(address,uint256)" [param1] $(cast parse-units 5) --rpc-url $RPC_URL

Send ETH (value transfer)

cast send --private-key $PRIVATE_KEY [recipient_address] --value $(cast to-wei 0.01 ether) --rpc-url $RPC_URL

Send with explicit gas price — useful on congested networks or when you want to speed up or slow down a transaction:

cast send --private-key $PRIVATE_KEY [contract_address] "setNumber(uint256)" 42 --gas-price $(cast to-wei 30 gwei) --rpc-url $RPC_URL

Estimate gas before sending


Before broadcasting a state-changing transaction it is good practice to estimate the gas cost. cast estimate performs a dry-run using eth_estimateGas and returns the gas units the transaction would consume — without submitting it to the network. No private key required.

cast estimate [contract_address] "mint(address,uint256)" [param1] $(cast parse-units 100 18) --rpc-url $RPC_URL

To convert the gas estimate into an ETH cost, multiply by the current base fee:

cast estimate [contract_address] "transfer(address,uint256)" [param1] 1000000000000000000 --rpc-url $RPC_URL
cast block latest --field baseFeePerGas --rpc-url $RPC_URL

cast estimate also works against --from a specific address so the simulation reflects the real caller's state (balance, allowances, etc.):

cast estimate --from [caller_address] [contract_address] "approve(address,uint256)" [spender] 1000000000000000000 --rpc-url $RPC_URL

Handle private keys and addresses


Cast also provides commands to handle private keys securely. To understand the importance of this, we must understand that in production environments, private keys should never be hardcoded, exposed in the code or even included in a .env file. Instead, they should be managed securely using the keystore.

The keystore is a secure storage mechanism that encrypts private keys and protects them with a password. To store a private key in the keystore, you can use the following command:

cast wallet import [key_name] --interactive

This command will prompt an interactive input in your terminal where you must paste the private key you want to store.

To see the list of private keys you have stored there, use:

cast wallet list

To use a private key stored in the keystore, for running a script you can use:

forge script script/general/DeploySimpleTest.s.sol --fork-url [rpc_url] --account [key_name] --sender [address_corresponding_to_pk] --broadcast

This command will use the private key associated with the specified key name to sign the transactions.

RPC configuration patterns


Typing --rpc-url on every command becomes tedious quickly. Cast supports three tiers of RPC configuration, applied in order of precedence (highest first):

1 — CLI flag (one-off override)

cast call [contract_address] "totalSupply()(uint256)" --rpc-url https://mainnet.infura.io/v3/YOUR_KEY

2 — Environment variable

Set ETH_RPC_URL once for the session and Cast will use it automatically:

export ETH_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/YOUR_KEY
cast block latest

3 — foundry.toml (project-level default)

Add an [rpc_endpoints] section to configure named networks. Cast picks up the file automatically when run inside the project directory:

foundry.toml

[rpc_endpoints] mainnet = "$" polygon = "$" sepolia = "$" local = "http://127.0.0.1:8545"

Then reference a named network with --rpc-url:

cast call [contract_address] "owner()(address)" --rpc-url polygon

Named network aliases are resolved via the [rpc_endpoints] map, so your commands stay readable and portable across team members who set their own API keys in .env.

Frequently Asked Questions


What is Cast in Foundry?

Cast is the CLI interaction tool in the Foundry smart contract development suite. It lets you call contract functions, send transactions, query blockchain state (balances, blocks, transactions, storage slots), convert data formats, decode calldata and event topics, estimate gas, and manage signing keys — all from the terminal, without writing JavaScript or Python scripts.

What's the difference between cast call and cast send?

cast call performs a read-only simulation (eth_call) — it does not change blockchain state, does not require a private key, and costs no gas. cast send broadcasts a real signed transaction (eth_sendRawTransaction) that modifies state and consumes gas. Use cast call to inspect data and cast send to execute writes.

How do I read data from a smart contract using Cast?

Use cast call [contract_address] "functionName(paramTypes)(returnTypes)" [params] --rpc-url $RPC_URL. Always include the return type in parentheses after the parameter list so Cast can decode the response. Example for ERC-20 balance: cast call 0xToken "balanceOf(address)(uint256)" 0xHolder --rpc-url $RPC_URL.

How do I convert hexadecimal to decimal with Cast?

Use cast to-dec 0x2a (outputs 42) or cast to-hex 42 (outputs 0x2a). For token amounts use cast format-units 1000000000000000000 18 (outputs 1.000000000000000000). For ETH: cast from-wei 1000000000000000000 (outputs 1.000000000000000000 ETH).

How do I compute a function selector with Cast?

Use cast sig "functionName(paramTypes)". Example: cast sig "transfer(address,uint256)" outputs 0xa9059cbb. To reverse-lookup an unknown selector: cast 4byte 0xa9059cbb queries the 4byte.directory database.

How do I decode calldata from a failed transaction?

Use cast decode-calldata "functionName(paramTypes)" [raw_calldata]. You can get the raw calldata from a block explorer. This is extremely useful for debugging reverts, especially when the frontend does not expose the underlying call parameters.

How should I handle private keys with Cast?

Never hardcode private keys or store them in .env files for production environments. Use Cast's encrypted keystore: cast wallet import [key_name] --interactive stores the key encrypted with a password. Reference it later with --account [key_name] and Cast will prompt for the password at runtime.

How do I estimate gas before sending a transaction?

Use cast estimate [contract_address] "functionName(types)" [params] --rpc-url $RPC_URL. It simulates the transaction and returns gas units. Multiply by cast block latest --field baseFeePerGas to get the ETH cost. Add --from [address] to simulate from a specific caller.

Can Cast work with any EVM network?

Yes. Cast is network-agnostic and works with Ethereum mainnet, testnets (Sepolia, Holesky), L2s (Arbitrum, Optimism, Base, Polygon), and local Anvil nodes. Set the RPC endpoint via --rpc-url, ETH_RPC_URL env var, or named aliases in foundry.toml's [rpc_endpoints] section.

What is cast parse-units and when should I use it?

cast parse-units [amount] [decimals] converts human-readable token amounts to their smallest unit. cast parse-units 1.5 18 outputs 1500000000000000000. Use it inside cast send commands with command substitution: $(cast parse-units 1.5 18) so you never have to count zeros manually.

Do I need to specify return types in cast call signatures?

It is strongly recommended. Without a return type, Cast prints raw hex. With return types (e.g., (uint256), (address,uint256)), Cast decodes and formats the output automatically. For tuple/struct returns, you can specify multiple values: "getReserves()(uint112,uint112,uint32)".

Extra resources


If you want to learn more about Cast and its capabilities, you can check the official documentation at Cast official docs

Furthermore, if you want to learn more about other Foundry tools, you can check the Foundry Anvil tutorial for local Ethereum node, where we explain how to use Anvil, the local Ethereum node from Foundry or if you are interested in the main tool of the suite, you can check the Foundry Forge tutorial for smart contract development and testing, where we explain how to use Forge, the main tool from Foundry for smart contract development, testing and deployment.

On the other hand, if you want to learn more about Solidity development and smart contract programming, you can check the Smart Contract Accounts guide where we explain how smart contract accounts work and how to create your own smart contract wallet with Solidity code examples. Finally, if you want to learn about signing data in smart contracts, you can check the EIP-712 signing data guide where we explain how to use EIP-712 to sign data in a secure and user-friendly way.