As they say, Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.

Apart of the speed another difference with Truffle or Hardhat is that the scripts and tests in Foundry are written in Solidity, not Javascript.

Let’s have a quick look how to install it in our Linux system and get to understand how to run all the tooling:

$ curl -L https://foundry.paradigm.xyz | bash 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  1765  100  1765    0     0   4412      0 --:--:-- --:--:-- --:--:--  4412
Installing foundryup...
######################################################################## 100.0%

Detected your preferred shell is bash and added foundryup to PATH. Run 'source /home/user/.bashrc' or start a new terminal session to use foundryup.
Then, simply run 'foundryup' to install Foundry.
$ . .bashrc 
$ foundryup 
foundryup: installing foundry (version nightly, tag nightly-1c415857dd7b617190834dbcb361506f6143fed4)
foundryup: downloading latest forge, cast and anvil
################################################################################################################################################# 100.0%
foundryup: downloading manpages
################################################################################################################################################# 100.0%
foundryup: installed - forge 0.2.0 (1c41585 2022-09-04T00:08:27.451724173Z)
foundryup: installed - cast 0.2.0 (1c41585 2022-09-04T00:08:27.451724173Z)
foundryup: installed - anvil 0.1.0 (1c41585 2022-09-04T00:08:27.527236025Z)
foundryup: done

Let’s check the options of forge, cast, and anvil before continue:

Forge is the Ethereum development and testing framework (Like hardhat or truffle):

$ forge 
forge 0.2.0 (1c41585 2022-09-04T00:08:27.451724173Z)
Build, test, fuzz, debug and deploy Solidity contracts.

USAGE:
    forge <SUBCOMMAND>

OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information

SUBCOMMANDS:
    bind                 Generate Rust bindings for smart contracts.
    build                Build the project's smart contracts. [aliases: b]
    cache                Manage the Foundry cache.
    clean                Remove the build artifacts and cache directories. [aliases: cl]
    completions          Generate shell completions script. [aliases: com]
    config               Display the current config. [aliases: co]
    coverage             Generate coverage reports.
    create               Deploy a smart contract. [aliases: c]
    debug                Debugs a single smart contract as a script. [aliases: d]
    flatten              Flatten a source file and all of its imports into one file. [aliases: f]
    fmt                  Formats Solidity source files.
    geiger               Detects usage of unsafe cheat codes in a foundry project and its dependencies.
    generate-fig-spec    Generate Fig autocompletion spec. [aliases: fig]
    help                 Print this message or the help of the given subcommand(s)
    init                 Create a new Forge project.
    inspect              Get specialized information about a smart contract. [aliases: in]
    install              Install one or multiple dependencies. [aliases: i]
    remappings           Get the automatically inferred remappings for the project. [aliases: re]
    remove               Remove one or multiple dependencies. [aliases: rm]
    script               Run a smart contract as a script, building transactions that can be sent onchain.
    snapshot             Create a snapshot of each test's gas usage. [aliases: s]
    test                 Run the project's tests. [aliases: t]
    tree                 Display a tree visualization of the project's dependency graph. [aliases: tr]
    update               Update one or multiple dependencies. [aliases: u]
    upload-selectors     Uploads abi of given contract to https://sig.eth.samczsun.com function selector database. [aliases: up]
    verify-check         Check verification status on Etherscan. [aliases: vc]
    verify-contract      Verify smart contracts on Etherscan. [aliases: v]

Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html

Cast is the CLI to interact with smart contracts on a low-level basis:

$ cast
cast 0.2.0 (1c41585 2022-09-04T00:08:27.451724173Z)
Perform Ethereum RPC calls from the comfort of your command line.

USAGE:
    cast <SUBCOMMAND>

OPTIONS:
    -h, --help       Print help information
    -V, --version    Print version information

SUBCOMMANDS:
    --abi-decode               Decode ABI-encoded input or output data [aliases: ad]
    --address-zero             Get zero address [aliases: address-zero, az]
    --calldata-decode          Decode ABI-encoded input data. [aliases: cdd]
    --concat-hex               Concatenate hex strings. [aliases: concat-hex, ch]
    --format-bytes32-string    Formats a string into bytes32 encoding.
    --from-bin                 Convert binary data into hex data. [aliases: from-bin, fb]
    --from-fix                 Convert a fixed point number into an integer. [aliases: from-fix, ff]
    --from-rlp                 Decodes RLP encoded data. Input must be hexadecimal.
    --from-utf8                Convert UTF8 text to hex. [aliases: from-utf8, --from-ascii, from-ascii, fu, fa]
    --from-wei                 Convert wei into an ETH amount. Consider using --to-unit. [aliases: from-wei, fw]
    --hash-zero                Get zero hash [aliases: hash-zero, hz]
    --max-int                  Get the maximum i256 value. [aliases: max-int, maxi]
    --max-uint                 Get the maximum u256 value. [aliases: max-uint, maxu]
    --min-int                  Get the minimum i256 value. [aliases: min-int, mini]
    --parse-bytes32-string     Parses a string from bytes32 encoding.
    --to-ascii                 Convert hex data to an ASCII string. [aliases: to-ascii, tas, 2as]
    --to-bytes32               Right-pads hex data to 32 bytes. [aliases: to-bytes32, tb, 2b]
    --to-checksum-address      Convert an address to a checksummed format (EIP-55). [aliases: to-checksum-address, --to-checksum, to-checksum, ta,
                                   2a]
    --to-dec                   Convert hex value into a decimal number. [aliases: to-dec, td, 2d]
    --to-fix                   Convert an integer into a fixed point number. [aliases: to-fix, tf, 2f]
    --to-hex                   Convert an integer to hex. [aliases: to-hex, th, 2h]
    --to-hexdata               Normalize the input to lowercase, 0x-prefixed hex. See --help for more info. [aliases: to-hexdata, thd, 2hd]
    --to-int256                Convert a number to a hex-encoded int256. [aliases: to-int256, ti, 2i]
    --to-rlp                   RLP encodes hex data, or an array of hex data
    --to-uint256               Convert a number to a hex-encoded uint256. [aliases: to-uint256, tu, 2u]
    --to-unit                  Convert an ETH amount into another unit (ether, gwei or wei). [aliases: to-unit, tun, 2un]
    --to-wei                   Convert an ETH amount to wei. Consider using --to-unit. [aliases: to-wei, tw, 2w]
    4byte                      Get the function signatures for the given selector from https://sig.eth.samczsun.com. [aliases: 4, 4b]
    4byte-decode               Decode ABI-encoded calldata using https://sig.eth.samczsun.com. [aliases: 4d, 4bd]
    4byte-event                Get the event signature for a given topic 0 from https://sig.eth.samczsun.com. [aliases: 4e, 4be]
    abi-encode                 ABI encode the given function argument, excluding the selector. [aliases: ae]
    access-list                Create an access list for a transaction. [aliases: ac, acl]
    age                        Get the timestamp of a block. [aliases: a]
    balance                    Get the balance of an account in wei. [aliases: b]
    basefee                    Get the basefee of a block. [aliases: ba, fee]
    block                      Get information about a block. [aliases: bl]
    block-number               Get the latest block number. [aliases: bn]
    call                       Perform a call on an account without publishing a transaction. [aliases: c]
    calldata                   ABI-encode a function with arguments. [aliases: cd]
    chain                      Get the symbolic name of the current chain. [aliases: ch]
    chain-id                   Get the Ethereum chain ID. [aliases: ci, cid]
    client                     Get the current client version. [aliases: cl]
    code                       Get the bytecode of a contract. [aliases: co]
    completions                Generate shell completions script [aliases: com]
    compute-address            Compute the contract address from a given nonce and deployer address. [aliases: ca]
    estimate                   Estimate the gas cost of a transaction. [aliases: e]
    etherscan-source           Get the source code of a contract from Etherscan. [aliases: et, src]
    find-block                 Get the block number closest to the provided timestamp. [aliases: f]
    gas-price                  Get the current gas price. [aliases: g]
    generate-fig-spec          Generate Fig autocompletion spec. [aliases: fig]
    help                       Print this message or the help of the given subcommand(s)
    index                      Compute the storage slot for an entry in a mapping. [aliases: in]
    interface                  Generate a Solidity interface from a given ABI. [aliases: i]
    keccak                     Hash arbitrary data using keccak-256. [aliases: k]
    lookup-address             Perform an ENS reverse lookup. [aliases: l]
    namehash                   Calculate the ENS namehash of a name. [aliases: na, nh]
    nonce                      Get the nonce for an account. [aliases: n]
    pretty-calldata            Pretty print calldata. [aliases: pc]
    proof                      Generate a storage proof for a given storage slot. [aliases: pr]
    publish                    Publish a raw transaction to the network. [aliases: p]
    receipt                    Get the transaction receipt for a transaction. [aliases: re]
    resolve-name               Perform an ENS lookup. [aliases: rn]
    rpc                        Perform a raw JSON-RPC request [aliases: rp]
    run                        Runs a published transaction in a local environment and prints the trace. [aliases: r]
    send                       Sign and publish a transaction. [aliases: s]
    shl                        Perform a left shifting operation
    shr                        Perform a right shifting operation
    sig                        Get the selector for a function. [aliases: si]
    storage                    Get the raw value of a contract's storage slot. [aliases: st]
    tx                         Get information about a transaction. [aliases: t]
    upload-signature           Upload the given signatures to https://sig.eth.samczsun.com. [aliases: ups]
    wallet                     Wallet management utilities. [aliases: w]

Find more information in the book: http://book.getfoundry.sh/reference/cast/cast.html

And finally, cast is the local Ethereum node (Like Ganache or Hardhat node):

$ anvil 


                             _   _
                            (_) | |
      __ _   _ __   __   __  _  | |
     / _` | | '_ \  \ \ / / | | | |
    | (_| | | | | |  \ V /  | | | |
     \__,_| |_| |_|   \_/   |_| |_|

    0.1.0 (1c41585 2022-09-04T00:08:27.527236025Z)
    https://github.com/foundry-rs/foundry

Available Accounts
==================

(0) 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (10000 ETH)
(1) 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 (10000 ETH)
(2) 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc (10000 ETH)
(3) 0x90f79bf6eb2c4f870365e785982e1f101e93b906 (10000 ETH)
(4) 0x15d34aaf54267db7d7c367839aaf71a00a2c6a65 (10000 ETH)
(5) 0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc (10000 ETH)
(6) 0x976ea74026e726554db657fa54763abd0c3a0aa9 (10000 ETH)
(7) 0x14dc79964da2c08b23698b3d3cc7ca32193d9955 (10000 ETH)
(8) 0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f (10000 ETH)
(9) 0xa0ee7a142d267c1f36714e4a8f75612f20a79720 (10000 ETH)

Private Keys
==================

(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
(1) 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
(2) 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
(3) 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
(4) 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
(5) 0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba
(6) 0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e
(7) 0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356
(8) 0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97
(9) 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6

Wallet
==================
Mnemonic:          test test test test test test test test test test test junk
Derivation path:   m/44'/60'/0'/0/


Base Fee
==================

1000000000

Gas Limit
==================

30000000

Genesis Timestamp
==================

1662332232

Listening on 127.0.0.1:8545

Now let’s build the template project.

First we have to initialize the forge template in an empty directory, so:

$ mkdir test
$ forge init
Initializing test...
Installing forge-std in "/tmp/test/lib/forge-std" (url: Some("https://github.com/foundry-rs/forge-std"), tag: None)
    Installed forge-std
    Initialized forge project.

That will create the following structure:

$ ls
foundry.toml  lib/  script/  src/  test/

And this is the template contract:

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

contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

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

Now we can build the default project (src/Counter.sol):

$ forge build 
[⠊] Compiling...
[⠰] installing solc version "0.8.16"
[⠘] Successfully installed solc 0.8.16
[⠒] Compiling 10 files with 0.8.16
[⠒] Solc 0.8.16 finished in 3.90s
Compiler run successful
$ forge test
[⠆] Compiling...
No files changed, compilation skipped

Running 2 tests for test/Counter.t.sol:CounterTest
[PASS] testIncrement() (gas: 28312)
[PASS] testSetNumber(uint256) (runs: 256, μ: 27531, ~: 28387)
Test result: ok. 2 passed; 0 failed; finished in 35.94ms

To deploy the contract we must call the contract script, in this case under script/Counter.s.sol. One of the main differences between forge and truffle or hardhat is that in forge, the test scripts are written in Solidity instead of JavaScript:

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

import "forge-std/Script.sol";

contract CounterScript is Script {
    function setUp() public {}

    function run() public {
        vm.broadcast();
    }
}

The function run() will be called when the contract is being deployed.

After the compile and test, we can run the scripts locally using one of the private keys given by anvil before and the local RPC node:

$ forge script script/Counter.s.sol:CounterScript --fork-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
[⠆] Compiling...
No files changed, compilation skipped
Script ran successfully.

And finally, deploy the contract on localnet:

$ forge create Counter --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
[⠆] Compiling...
No files changed, compilation skipped
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Transaction hash: 0xe065e23fd862dfa2283ba4c87a77539dec4e3a972e5d90de265cb3b5d4de7ae6

With the deployed address of the smart contract (0x5FbDB2315678afecb367f032d93F642f64180aa3), we can retrieve the smart contract bytecode directly from the local blockchain:

$ cast code 0x5FbDB2315678afecb367f032d93F642f64180aa3
0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220f4a9b22e7a2d64c24355b4e7a6f8c62115ca728f26fc2a1e98e364ee91f794fa64736f6c63430008100033

To make a low-level call to a method of the smart contract, we can use cast call and cast send.

To initialize the number:

$ cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 "setNumber(uint256)" "34" --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

blockHash               0x9bc16ad14fc77a9a295145f69d2aa0ea43c494655620d2facce458f16a2b09cd
blockNumber             3
contractAddress         
cumulativeGasUsed       26394
effectiveGasPrice       3671048612
gasUsed                 26394
logs                    []
logsBloom               0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
root                    
status                  1
transactionHash         0xd438f5b0dbd78ccbe3935fffc0b52c563cb5a654186b89a6fd1815177ce92e45
transactionIndex        0
type

To call increment():

$ cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 "increment()" --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

blockHash               0xe94baa8582bf0893a4cd69913f2d50a33eed5c41b7e2403bfa885f88437e89dd
blockNumber             4
contractAddress         
cumulativeGasUsed       26304
effectiveGasPrice       3587314629
gasUsed                 26304
logs                    []
logsBloom               0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
root                    
status                  1
transactionHash         0x9783814115f16e979f377d3aea559afc5ce4a4dc4afac1161531f9bd1a255b5e
transactionIndex        0
type        

As mentioned in the Solidity documentation, the compiler creates getter() functions for all public state variables, so we can now call number() to validate that all methods worked successfully.

To call cast call we do not need to specify a private key. Functions to just get data from the blockchain and do not modify them, do not consume gas… so they are free 🙂

$ cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 "number()"
0x0000000000000000000000000000000000000000000000000000000000000023

Seems it worked! Lets really check if 0x23 is 35 in decimal notation.

$ python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44) 
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0x23
35
>>>

Mission accomplished!

How useful was this post?

Click on a star to rate it!

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Leave a Reply