Deploy on Zircuit

A tutorial for deploying a smart contract on Zircuit using Foundry

This tutorial will guide you through the complete process of setting up Foundry, creating a smart contract, and deploying it to the Zircuit testnet. Foundry is a powerful, fast, and portable toolkit for Ethereum application development written in Rust.

Prerequisites

Before starting, ensure you have:

  • A Unix-like operating system (macOS, Linux, or WSL on Windows)

  • Git installed

  • A wallet with some testnet ETH bridged to Zircuit testnet

  • Basic understanding of Solidity and blockchain concepts

What is Foundry?

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development. It consists of:

  • Forge: Ethereum testing framework (like Truffle, Hardhat)

  • Cast: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data

  • Anvil: Local Ethereum node, akin to Ganache, Hardhat Network

  • Chisel: Fast, utilitarian, and verbose Solidity REPL

1. Set Up Foundry

First, we'll install Foundry using the official installer script:

# Download and install Foundry
curl -L https://foundry.paradigm.xyz | bash

# Reload your shell configuration to make foundryup available
source ~/.zshrc  # for zsh users
# OR
source ~/.bashrc  # for bash users

# Install the latest version of Foundry tools
foundryup

What this does:

  • Downloads the Foundry installer script

  • Installs foundryup, the Foundry toolchain installer

  • Updates your shell to recognize the new commands

  • Installs or updates forge, cast, anvil, and chisel

Verify installation:

forge --version

2. Create a New Counter Project

Initialize a new Foundry project with the counter template:

# Create a new Foundry project called 'counter'
forge init counter

# Navigate into the project directory
cd counter

What this creates:

counter/
├── foundry.toml        # Foundry configuration file
├── src/               # Smart contract source files
│   └── Counter.sol    # Main contract
├── script/            # Deployment scripts
│   └── Counter.s.sol  # Deployment script
├── test/              # Test files
│   └── Counter.t.sol  # Contract tests
└── lib/               # Dependencies (like node_modules)

Project structure explained:

  • src/: Contains your Solidity smart contracts

  • script/: Contains deployment scripts written in Solidity

  • test/: Contains test files for your contracts

  • lib/: Contains external dependencies

  • foundry.toml: Configuration file for compiler settings, networks, etc.

3. Set Up Your Private Key for Deployment

You need to securely store your private key for contract deployment:

# Import your private key securely
cast wallet import defaultKey --interactive

What this does:

  • Prompts you to enter your private key securely (won't display on screen)

  • Encrypts and stores the key locally under the name "defaultKey"

  • Requires a password to encrypt the stored key

Alternative methods:

# Using environment variable (less secure)
export PRIVATE_KEY=0x...

# Using keystore file
cast wallet import mykey --keystore /path/to/keystore

4. Compile Your Smart Contract

Compile all contracts in your project:

forge compile

What this does:

  • Compiles all .sol files in the src/ directory

  • Generates ABI (Application Binary Interface) files

  • Creates bytecode for deployment

  • Checks for compilation errors and warnings

  • Outputs artifacts to out/ directory

Expected output:

[⠊] Compiling...
[⠢] Compiling 3 files with 0.8.19
[⠆] Solc 0.8.19 finished in 1.2s
Compiler run successful!

5. Review the Contract and Deployment Script

Let's examine the smart contract and deployment script in detail:

src/Counter.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Counter {
    // State variable to store the counter value
    uint256 public number;

    // Function to set the counter to a specific value
    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    // Function to increment the counter by 1
    function increment() public {
        number++;
    }
}

Contract explanation:

  • uint256 public number: A public state variable that automatically generates a getter function

  • setNumber(): Allows setting the counter to any value

  • increment(): Increases the counter by 1

  • All functions are public, meaning anyone can call them

script/Counter.s.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";

contract CounterScript is Script {
    Counter public counter;

    function setUp() public {}

    function run() public {
        // Start recording transactions for broadcast
        vm.startBroadcast();
        
        // Deploy the Counter contract
        counter = new Counter();
        
        // Stop recording transactions
        vm.stopBroadcast();
    }
}

Deployment script explanation:

  • Inherits from Script to access deployment utilities

  • vm.startBroadcast(): Begins recording transactions to broadcast to the network

  • new Counter(): Deploys a new instance of the Counter contract

  • vm.stopBroadcast(): Stops recording transactions

6. Deploy Your Smart Contract

Deploy the contract to Zircuit Garfield Testnet:

forge script script/Counter.s.sol:CounterScript \
    --rpc-url https://garfield-testnet.zircuit.com \
    --account defaultKey \
    --broadcast

Command breakdown:

  • forge script: Command to run deployment scripts

  • script/Counter.s.sol:CounterScript: Path to script file and contract name

  • --rpc-url: The RPC endpoint for Zircuit testnet

  • --account defaultKey: Uses the imported private key

  • --broadcast: Actually sends transactions to the network

Expected output:

[⠊] Compiling...
No files changed, compilation skipped
Enter keystore password:
Script ran successfully.

## Setting up 1 EVM.

==========================

Chain 48898

Estimated gas price: 0.000000509 gwei

Estimated total gas used for script: 203856

Estimated amount required: 0.000000000103762704 ETH

==========================

##### 48898
✅  [Success] Hash: 0x59bf94e4055ee2c4a71b9e6a7b7589ad3a5831ac38717c5f0d488eb4ed365a77
Contract Address: 0x6E69d4f9bc6a3E2f67d2D86877800482A8cdca40
Block: 8549829
Paid: 0.000000000039987315 ETH (156813 gas * 0.000000255 gwei)

✅ Sequence #1 on 48898 | Total Paid: 0.000000000039987315 ETH (156813 gas * avg 0.000000255 gwei)

Copy the Contract Address from the output - you'll need it for the next step!

7. Interact with Your Smart Contract

Now that your contract is deployed, you can interact with it using Cast.

Execute a State-Changing Function

To call the increment() function (which costs gas):

cast send <CONTRACT_ADDRESS> "increment()" \
    --rpc-url https://garfield-testnet.zircuit.com \
    --account defaultKey

Replace <CONTRACT_ADDRESS> with your actual contract address from step 6.

What this does:

  • Sends a transaction to call the increment() function

  • Uses your imported private key to sign the transaction

  • Pays gas fees for the transaction

Expected output:

blockHash            0x97162a12dc900daf598e18c7a026d0b7bea5b121fc20bd99600292b53ba8148b
blockNumber          8550305
contractAddress      
cumulativeGasUsed    91965
effectiveGasPrice    255
from                 0xbd9B49deFc88AC16D7fC0F7FE6Eb7E0F54F6317f
gasUsed              43482
...

Read Contract State

To read the current value of number (free, no gas required):

cast call <CONTRACT_ADDRESS> "number()" \
    --rpc-url https://garfield-testnet.zircuit.com

Expected output:

0x0000000000000000000000000000000000000000000000000000000000000001

This hexadecimal output represents the number 1, showing that our increment worked!

To convert hex to decimal:

cast --to-dec 0x0000000000000000000000000000000000000000000000000000000000000001
# Output: 1

Additional Interaction Examples

Set the counter to a specific value:

cast send <CONTRACT_ADDRESS> "setNumber(uint256)" 42 \
    --rpc-url https://garfield-testnet.zircuit.com \
    --account defaultKey

Check the balance of your deployer address:

cast balance <YOUR_WALLET_ADDRESS> \
    --rpc-url https://garfield-testnet.zircuit.com

Get transaction details:

cast tx <TRANSACTION_HASH> \
    --rpc-url https://garfield-testnet.zircuit.com

8. Advanced Features and Next Steps

Running Tests

Foundry includes a powerful testing framework:

# Run all tests
forge test

# Run tests with verbose output
forge test -vvv

# Run specific test
forge test --match-test testIncrement

Code Coverage

Generate test coverage reports:

forge coverage

Gas Reporting

Get detailed gas usage reports:

forge test --gas-report

Configuration

Customize your foundry.toml for different networks:

[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# Zircuit testnet configuration
[rpc_endpoints]
zircuit_testnet = "https://garfield-testnet.zircuit.com"

[etherscan]
zircuit_testnet = { key = "${ZIRCUIT_API_KEY}" }

Troubleshooting

Common Issues

  1. "Account not found": Make sure you've imported your private key correctly

  2. "Insufficient funds": Ensure your wallet has enough testnet ETH

  3. "Compilation failed": Check your Solidity syntax and version compatibility

  4. "RPC connection failed": Verify the RPC URL is correct and accessible

Getting Help

Verifying Contracts

Last updated

Was this helpful?