Skip to main content

Overview

The EVM smart contract workflow uses Foundry under the hood — the same toolchain used by Uniswap, OpenSea, and most serious Solidity teams. You get forge for compilation and testing, anvil for local development, and cast for chain interaction, all accessible through the AI chat without touching the terminal.

Project Structure

A standard Foundry project looks like this:
my-project/
├── foundry.toml
├── src/
│   └── MyToken.sol
├── test/
│   └── MyToken.t.sol
└── lib/
    ├── forge-std/
    └── openzeppelin-contracts/
You can create this yourself or ask the AI:
"Create a Foundry project with an ERC-20 token"

foundry.toml

A minimal config:
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
optimizer = true
optimizer_runs = 200

[rpc_endpoints]
local = "http://127.0.0.1:8545"
If you’re using OpenZeppelin or other libraries, add remappings:
remappings = [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/"
]

Compiling Contracts

Compile an Entire Project

If you have a foundry.toml:
"Compile my project"
This runs forge build on the entire project. Dependencies like OpenZeppelin are installed automatically.

Compile a Single Contract

For quick prototyping without a full project:
"Compile this contract: [paste Solidity code]"

Compilation Output

After compilation you get:
  • Bytecode — the compiled contract ready for deployment
  • ABI — the interface definition for interacting with the contract
  • Bytecode file path — used by the deployment tool

OpenZeppelin Imports

Use the standard import format:
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
Dependencies are resolved automatically. No npm install, no forge install.

Testing Contracts

Tests use Foundry’s testing framework (forge-std). Write tests in Solidity:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import "../src/MyToken.sol";

contract MyTokenTest is Test {
    MyToken token;
    address owner = address(this);
    address user = address(0x1);

    function setUp() public {
        token = new MyToken();
    }

    function testInitialSupply() public view {
        assertEq(token.totalSupply(), 1000000 * 10 ** 18);
    }

    function testMint() public {
        token.mint(user, 1000);
        assertEq(token.balanceOf(user), 1000);
    }

    function testMintOnlyOwner() public {
        vm.prank(user);
        vm.expectRevert();
        token.mint(user, 1000);
    }
}
Run tests:
"Run my tests"
"Run tests with verbose output"
"Run only the testMint test"
You get pass/fail counts, gas usage per test, and full stack traces for failures.

Deploying Contracts

"Deploy MyToken to local"
Behind the scenes:
  1. Starts Anvil if not already running
  2. Switches wallet to Local EVM
  3. Funds your account with test ETH
  4. Deploys the compiled bytecode
  5. Returns the contract address

To Testnet

"Deploy MyToken to Sepolia"
Make sure you have testnet ETH from a faucet.

To Mainnet

"Switch to Ethereum mainnet and deploy MyToken"
Mainnet deployments cost real money. Contracts are immutable once deployed.

With Constructor Arguments

"Deploy with constructor args ['MyToken', 'MTK', 1000000]"
Arguments are ABI-encoded automatically based on the constructor signature.

Interacting with Deployed Contracts

Read Functions (Free, No Gas)

"Read totalSupply from 0x..."
"Call balanceOf(0x...) on the contract at 0x..."

Write Functions (Costs Gas)

"Call mint(0x..., 1000) on the token contract"
"Call approve(0x..., 1000000) on the token"
The AI handles ABI encoding. You provide the function name and arguments in a readable format.

Using Cast (Low-Level)

For advanced chain interaction:
"Use cast to decode this calldata: 0x..."
"Use cast to convert 1.5 ETH to wei"

Gas Estimation

"Estimate gas for deploying this contract"
Gas prices fluctuate with network demand. For non-urgent mainnet transactions, consider waiting for lower gas prices.

Security Auditing

"Audit my contract"
This uses Aderyn to check for 90+ vulnerability patterns including reentrancy, access control issues, unchecked return values, and gas optimizations. For production contracts, also get a professional manual audit.

Common Patterns

PatternExample Prompt
ERC-20 Token"Create an ERC-20 with minting, burning, and pausable"
ERC-721 NFT"Create an NFT with metadata URI and royalties"
ERC-1155 Multi-Token"Create an ERC-1155 for a game with multiple item types"
Upgradeable (UUPS)"Create an upgradeable ERC-20 using UUPS"
Access Control"Add role-based access control to this contract"

Deployment Checklist

Before deploying to mainnet:
  1. All tests pass with no skipped tests
  2. Security audit is clean (no high/medium findings)
  3. Compiled with optimization enabled
  4. Constructor arguments verified
  5. Sufficient funds for gas
  6. Correct network selected

Troubleshooting

Common issues: missing semicolons, Solidity version mismatches (use ^0.8.24), import path errors (use @openzeppelin/contracts/... format), type mismatches.
Check: wallet connected, sufficient gas funds, constructor args match expected types, contract under 24KB size limit.
Verify: correct contract address, correct network, function name and arg types match ABI, require() conditions satisfied.
Check setUp() initialization, use vm.prank() for different addresses, use vm.expectRevert() before failing calls, run with verbose output.