Install and Setup Foundry for Solidity¶
The last toolkit is the newest one and the last one I want to show you in this section.
It's Foundry, a completely fresh rewrite in Rust. It's blazing fast and not dependent on JavaScript at all.
Install Foundry¶
To install Foundry, you run an actual shell script on your computer.
You can, for example, create a new folder for foundry in our nft-project folder:
mkdir foundry
cd foundry
curl -L https://foundry.paradigm.xyz | bash
source ~/.bashrc
foundryup
Now you will have foundry installed, which comes with three subcommands:
- forge: the build, test, debug, deploy smart contracts
- anvil: the foundry equivalent of Ganache
- cast: low level access to smart contracts (a bit of a truffle console equivalent)
Initializing a new Foundry Project¶
To get started with foundry, initialize a new project in the foundry directory:
forge init .
This will download a starter repository and setup everything for the first use.
It looks a bit different than hardhat and truffle:
- contracts are in the
src
folder - tests are in the
test
folder and are solely solidity tests - scripts are in the
scripts
folder and are solidity files - dependencies are added as git submodules into the
lib
folder - Project is configured using the
foundry.toml
file - After building (which you'll see in a second), the contract artifacts are in the
out
folder
It doesn't depend on JavaScript at all. Everything is a git submodule. And its also blazing fast.
Building and Testing using Forge¶
To build the smart contracts, simply run
forge build
It will compile all files and put the ABI, bytecode and AST as json files into subfolders with the name of the contracts in the out folder
Let's see how we can add our NFT into the folders...
Adding our NFT to Foundry¶
First thing is to delete the existing contract, test and script:
rm src/*.sol test/*.sol script/*.sol
Then simply copy over the Spacebears.sol contract from Hardhat. If you try to run forge build, it will fail, because the package for openzeppelin isn't installed.
Here's one of the biggest differences to HardHat and Truffle: Foundry is not a JavaScript based framework and everything works with git submodules. Here we need to install github-user/repo-name
, which is openzeppelin/openzeppelin-contracts
for https://github.com/OpenZeppelin/openzeppelin-contracts
!
Install openzeppelin with
forge install openzeppelin/openzeppelin-contracts
Which will fail, because we have changes in our branch, which we need to commit first:
If you run git status
it will tell you what changed:
We added the Spacebear contract and removed all other contracts from the directories.
git add .
git commit -a -m "Added spacebear contract and removed everything else"
This should do the trick. Now, let's try again:
forge install openzeppelin/openzeppelin-contracts
We also need to change the imports, as we're not importing from the npm module @openzeppelin/..., we're importing from openzeppelin-contracts...:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "openzeppelin-contracts/token/ERC721/ERC721.sol";
import "openzeppelin-contracts/access/Ownable.sol";
import "openzeppelin-contracts/utils/Counters.sol";
contract Spacebear is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("Spacebear", "SBR") {}
function _baseURI() internal pure override returns (string memory) {
return "https://ethereum-blockchain-developer.com/2022-06-nft-truffle-hardhat-foundry/nftdata/";
}
function safeMint(address to) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
}
function buyToken() public payable {
uint256 tokenId = _tokenIdCounter.current();
require(msg.value == tokenId * 0.1 ether, "Not enough funds sent");
_tokenIdCounter.increment();
_safeMint(msg.sender, tokenId);
}
// The following functions are overrides required by Solidity.
function _burn(uint256 tokenId) internal override(ERC721) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
pure
override(ERC721)
returns (string memory)
{
return string(abi.encodePacked(_baseURI(),"_",tokenId+1,".json"));
}
}
Now the compilation works:
Let's have a look in the next lecture how we can do unit-testing with Foundry. It's a lot of fun, because foundry is
- blazing fast
- allows VM modifications from within solidity