How to deploy smart contracts using Foundry

How to deploy smart contracts using Foundry

Learn how you can deploy your contracts both on-chain and locally

Introduction

If you’ve been following this series so far, you already know how to write and compile smart contracts in Foundry. You also know how to use keystores to safely store and use your private keys. Now it is time to learn how you can deploy smart contracts. This article will cover both local and on-chain deployment.

Which way, Solidity dev?

There are two ways you can deploy your contracts using Foundry- the first is by using the forge create command and the second is by using Solidity scripts. Solidity scripting is a way to declaratively deploy contracts using Solidity, instead of using the more limiting and less user friendly forge create. If you are deploying a simple contract which has no constructor arguments and no reliance on other contracts that need to be deployed, it makes sense to just use forge create instead of making a separate script.

If you’re coming from a Hardhat background, think of Solidity scripts as the scripts you write in Hardhat, except they’re in Solidity instead of JavaScript, and they are run on the fast Foundry EVM backend, which provides dry-run capabilities.

We’ll cover both ways in this article.

Initial Setup

If you haven’t created a foundry project, go ahead and create one using this command:

forge init project-name

Open src/Counter.sol and add a constructor function to the contract

constructor (uint _initNumber){
    number= _initNumber;
}

Your final code should look like this:

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

contract Counter {
    uint256 public number;
    constructor (uint _initNumber){
        number= _initNumber;
    }
    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}

Navigate to your project’s folder using your terminal and compile the contract like so:

cd project-name
forge build
# you can also use forge compile

You should see something like this in your terminal:

We’ll use anvil to start a local blockchain with chainId 31337. Go to your terminal and run:

anvil

You’ll then see a list of accounts and their corresponding private keys which already have a balance of 10000 ETH on chain ID 31337. Copy one of these private keys, make a .env file in the root of your project and paste it there like so:

LOCAL_PRIVATE_KEY=thecopiedkey

We’re not using keystores for local deployments since this is a known private key which everyone sees when using Foundry and is only used locally.

Now enter the URL of a Sepolia RPC endpoint in the .env file. I’m using a public RPC in this example:

SEPOLIA_RPC_URL=https://1rpc.io/sepolia

Now open your foundry.toml file. It should be present in the root of the project. At the end of the file, add:

[rpc_endpoints]
sepolia = "${SEPOLIA_RPC_URL}"

This creates an RPC alias for Sepolia and provides cheatcodes to access the RPC endpoint we added.

Keep your anvil terminal running. Open a new terminal and run:

source .env

This updates your terminal’s environment variables and adds the ones we added in the .env file.

Awesome! Now let’s see how we can deploy our contract

Deploying via forge create

The contract that needs to be deployed is a pretty simple one and it takes one constructor argument. Let’s see how we can deploy it using forge create

Local Deployment

In your terminal, run:

forge create --rpc-url http://localhost:8545 --private-key $LOCAL_PRIVATE_KEY src/Counter.sol:Counter --constructor-args 23

We’ve specified the constructor argument as 23 here, so the value of number in our contract will be set to 23. If the transaction goes through, you’ll see something like this:

Sepolia Deployment

This process is pretty similar to deploying locally. After all, we’re only deploying to a chain which has a different ID. The only things we’ll need are an account which has Sepolia ETH and an RPC URL. For accessing an account which has Sepolia ETH, we’ll use keystores. We already set up one such account in the previous article and named it “dev”. You can read it here.

In your terminal, run this command:

forge create --rpc-url $SEPOLIA_RPC_URL  --account dev src/Counter.sol:Counter --constructor-args 23

This time we’re using our keystore, so it will ask you to enter its password. If you enter the right password and the transaction goes through, you’ll see something like this:

Deploying via scripts

Open script/Counter.sol. For the most part, the required code is already written there. Let’s go through it step-by-step

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

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

contract CounterScript is Script {
    Counter public counter;

    function setUp() public {}

    function run() public {
        vm.startBroadcast();

        counter = new Counter(); 

        vm.stopBroadcast();
    }
}

The import statements get Script and console from the forge standard library, and the Counter contract we modified.

We then specify that the contract in this file inherits the Script contract from the forge standard library. Notice that everything in Foundry is a Solidity smart contract.

Then, we declare a new variable counter of the type Counter (our contract)

setUp is a function that runs every time we run a script. Since we don’t have anything to set up right now, we’ll leave this empty.

By default, scripts are executed by calling the function named run. In run, everything between vm.startBroadcast and vm.stopBroadcast creates transactions that can later be signed and sent on-chain- local, testnet, or mainnet.

If you have the right extensions for Solidity, you might be seeing an error in the line which creates a new instance of Counter.

This is because we now need to specify a value of type uint256 as the argument of our contract’s constructor. Just add a number in the brackets like so:

counter = new Counter(2);

This will set the number to be 2 when the contract is deployed.

Local Deployment

Just run this command in your terminal:

forge script script/Counter.s.sol:CounterScript --fork-url http://localhost:8545  --broadcast --private-key $LOCAL_PRIVATE_KEY

Here http://localhost:8545 is the URL that is used to fetch the state of the local chain (id 31337) and then sending the transaction to it. You should see something like this:

and in the terminal running anvil:

Sepolia Deployment

Again, this process is pretty similar to deploying locally and we only need an account which has Sepolia ETH along with an RPC URL for Ethereum Sepolia. We’ll use keystores for this case too.

Open your terminal and write:

forge script --chain sepolia script/Counter.s.sol:CounterScript --rpc-url $SEPOLIA_RPC_URL  --broadcast --account dev

It will ask you to enter the password of the keystore (dev). If you enter the correct password, it attempts to send your transaction using the account corresponding to the keystore. If it succeeds, you’ll get something like this

Conclusion

That’s pretty much it! You now know how you can deploy your contracts locally and on-chain using keystores and environment variables. In the next lesson, we’ll learn how we can fork Ethereum Mainnet to get the entire state of the blockchain locally. Sounds interesting? Well that’s because it is! Stay tuned, see you all soon. Thanks a lot for reading to the end 🫡🫡