GitHub Repo:- https://github.com/PradhumnaPancholi/Figbot Hey everyone! A little while ago, I was learning as it has fantastic tools for developing and auditing smart contracts. And although I loved the experience, I soon learned that it is in the clandestine development stage. This means that casual/individual users can not depend on maintainers for support and updates. Dapp Tools Then I stumbled upon . It has everything that Dapp Tools offers apart from built-in symbolic execution (which is not a problem for me as I use e by Trail of Bits ). And this is auditing related hence not a hindrance in smart contract development by any stretch of the imagination. Foundry Manticor After working with Foundry for a bit, I enjoyed the experience and wanted to share that with others. Hence, this article. This article will go through the benefits of Foundry, the installation process, developing an NFT (because everyone is interested in that), testing the contract, and deploying it with . Figment Datahub Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. Foundry is made up of three components: Ethereum testing framework (like Truffle, Hardhat, and Dapptools). Forge: Swiss army knife for interacting with EVM smart contracts, sending transactions, and getting chain data. Cast: local Ethereum node, akin to Ganache, Hardhat Network Anvil: Today’s focus is going to be on But I will be posting in-depth articles on Caste and Anvil in the upcoming weeks. Forge. Why Foundry: There are many smart contract development tools like Truffle, Hardhat, and Brownie. But one of my primary reasons for looking into Dapp Tools in the first place was native Solidity tests. Writing smart contracts is not hard when switching between frameworks like Hardhat and Brownie. And they are incredible tools with plugins, but one needs to be well versed in JavaScript/TypeScript and Python to perform testing. Foundry allows us to write our tests natively in Solidity. This saves a lot of time onboarding new developers and makes the process smoother. In my experience of helping people navigate their way into smart contracts development, I have learned that the best and most efficient way for junior developers to engage with DAO/community-maintained projects is by writing tests and learning about the code-base itself. I remember once mentioned that they used the same approach while developing Alchemix Finance on . Scupy Trooples Bankless In addition to that, built-in fuzzing, cheat codes, Cast, and Anvil make it a solid suite for testing smart contracts. There will be more detailed articles on those components coming soon. [Easy to integrate static analyzer] Let’s dive in and build an NFT project now. Installation: If you are on Mac or Linux, all you need to do is run two commands: curl -L https://foundry.paradigm.xyz | bash foundryup Make sure to close the terminal before running . foundryup And Voila! You are all done. For Windows, you need to have Rust installed and then : cargo install --git https://github.com/foundry-rs/foundry --locked Project Setup: For this article, we will be creating a simple NFT project called Figbots. Start by creating a directory called “Figbots.” And run once you are inside the directory. This command will create a foundry project for you with initialized. forge init git Let’s take a quick look at the folder structure. You have three primary folders, namely src, lib, and test. Very much self-explanatory here, you write your contracts in , tests in , and contains all the libraries you installed, e.g., OpenZeppelin. In addition to that, you get which contains all the configurations just like and if you have used those frameworks. Another sweet thing is .github, where you can write your Github Actions. I find it really helpful for tests when working in a team. src test lib foundry.toml hardhat.config.js brownie-config.yaml Let’s start building! We will create a simple NFT called Figbot with a limited supply, cost (for minting), and withdrawal. With this approach, we can cover edges for different tests. First of all, rename and to and respectively. Now, we can not write smart contracts without Openzeppelin, can we? Contract.sol test/Contract.t.sol Figbot.sol Figbot.t.sol Installing libraries with Foundry is slightly different than Hardhat and Brownie. We don’t have npm or pip packages. We install libraries directly from the Source (GitHub repo) in Foundry. forge install Openzeppelin/openzeppelin-contracts Now we can import the ERC721URIStorage.sol extension to create our NFT. To check that everything is alright, we can run the command , and it will compile our project. The compiler will yell at you if there is something wrong. Otherwise, you will get a successful compile. forge build Managing dependencies Just like any other package manager, Forge allows you to use , and to manage your dependencies. forge install <lib>, forge remove <lib> forge update <lib> Let’s Complete The NFT Contract: We will be using three contracts from the Openzeppelin. Counters, ERC721URIStorage, and Ownable. Time to upload our to IPFS using . We use the Ownable contract to set deploying address and have access to modifier to allow only the owner to withdraw funds. to help us with token id(s) and to keep the NFT contract simple. asset Pinata owner onlyOwner Counters ERC721URIStorage Setting state variable: to 100 MAX_SUPPLY to 0.69 ether COST to CID, we receive from Pinata TOKEN_URI Using Counter for token id: using Counters for Counters.Counter; Counters.Counter private tokenIds; ERC721 constructor: constructor() ERC721(“Figbot”, “FBT”) {} Mint function: Check if is greater than msg.value COST Check if is greater or equal to tokenIds.current() MAX_SUPPLY Perform and _safeMint _setTokenURI Withdraw function: function withdrawFunds() external onlyOwner { uint256 balance = address(this).balance; require(balance > 0, "No ether left to withdraw"); (bool success, ) = (msg.sender).call{value: balance}(""); require(success, "Withdrawal Failed"); emit Withdraw(msg.sender, balance); } TotalSupply function: function totalSupply() public view returns (uint256) { return _tokenIds.current(); } Testing The Contract: As we all know, testing our smart contracts is really important. In this section, we will be writing some tests to get a solid understanding of and get used to writing tests in native solidity. We will be three (I love them!) to manage account states to fit our test scenario. forge test Foundry cheat codes We will be testing for the following scenarios: Max Supply Successful mint Failed mint due to insufficient balance Withdraw (by owner) Cheatcodes As we can have complex logic in our smart contracts. And they are expected to behave differently depending on the state, the account used to invoke, time, etc. To deal with such scenarios, we can use cheatcodes to manage the state of the blockchain. We can use these cheatcodes using instance, which is a part of Foundry’s library. vm Test We will be using three cheatcodes in our tests : : Sets for all subsequent calls until is called. startPrank msg.sender stopPrank : stopPrank Stops an active prank started by , resetting and to the values before was called. startPrank msg.sender tx.origin startPrank : Sets the balance of an address provided address to the given balance. deal Setup Foundry comes with a built-in testing library. We start by importing this test library, our contract (the one we want to test), defining the test, setting variables, and function. setUp pragma solidity ^0.8.13; import"forge-std/Test.sol"; import "../src/Figbot.sol"; contract FigbotTest is Test { Figbot figbot; address owner = address(0x1223); address alice = address(0x1889); address bob = address(0x1778); function setUp() public { vm.startPrank(owner); figbot = new Figbot(); vm.stopPrank(); } } For state variables, we create a variable of type . This is also the place where I like to define user accounts. In Foundry, you can describe an address by using the syntax . you can use any four alphanumeric characters for this. I have created the accounts named owner, Alice, and bob, respectively. figbot Figbot address(0x1243) Now our function. This is a requirement for writing tests in Foundry. This is where we do all the deployments and things of that nature. I used the cheatcode to switch the user to the “owner.” By default, Foundry uses a specific address to deploy test contracts. But that makes it harder to test functions with special privileges like . Hence, we switch to the “owner” account for this deployment. setUp startPrank withdrawFunds Test MaxSupply: Starting with a simple assertion test to learn Foundry convention. By convention, all the test functions must have the prefix . And we use to test if two values are equal. test assertEq We call our function and test if the result value is 100, as we described in our contract. And we use to run our tests. MaxSupply forge test And Voila!!! we have a passed test. Test Mint: Now that we have written a simple test, let’s write one with cheatcodes. The primary function of our contract. Switch user account to Alice. Set Alice’s balance to 1 ether Call the mint function Check if Alice is 1 balanceOf TestFail Mint: We have another testing function used for tests that we expect to fail. The prefix used for such a test is . We will test if the function reverts if the caller has insufficient funds. testFail mint Switch user account to Bob Set Bob’s balance to 0.5 ether (our NFT is 0.69 ether) Call the mint function (it will be reverted due to not having enough funds) Check if Bob is 1 balanceOf Because mint didn’t go through, the balance of Bob is not going to be 1. Hence, it will fail, which is exactly what we are used for. So when you run , it will pass. testFail forge test Test Withdraw: Here we will test a function that only the “owner” can successfully perform. For this test, we will : Switch user to Bob Give Bob’s account the balance of 1 ether Mint a Figbot from Bob’s account ( this will give the contract a balance of 0.69 ether ) Switch the user to the owner account Perform function ( if successful, it should make the owner’s balance 0.69 ether) withdrawFunds To verify, we assert if the owner’s balance is 0.69 ether Deployment: Now that we have tested our contract, it is time to deploy it. We need private keys to a wallet (with some Rinkeby test ETH) and an RPC URL. For our RPC URL, we will use . Figment DataHu Figment DataHub provides us with infrastructure to develop on Web 3. It supports multiple chains like Ethereum, Celo, Solana, Terra, etc. Setting up Figment DataHub: Create an account on . Figment DataHub Click on “Create New App.” Fill in the app name. Choose “Staging” for the environment. Select “Ethereum” from the provided options. You can get your RPC URL for Rinkeby from under the “Protocols” tab. Open your terminal to enter both of these things as environment variables. export FIG_RINKEBY_URL=<Your RPC endpoint> export PVT_KEY=<Your wallets private key> Once we have the environment variables, we are all set to deploy forge create Figbot --rpc-url=$FIG_RINKEBY_URL --private-key=$PVT_KEY Verification: We are almost done here. So far, we have written, tested, and deployed a smart contract with Foundry and Figment DataHub. But we are not entirely done just yet. We are now going to verify our contract. We will need to set up our API key for that. Etherscan export ETHERSCAN_API=<Your Etherscan API Key> And now we can verify our smart contract. forge verify-contract --chain-id <Chain-Id> --num-of-optimizations 200 --compiler-version <Compiler Version> src/<Contract File>:<Contract> $ETHERSCAN_API Congratulations! Now you can write, test, and deploy smart contracts using Foundry. I hope you enjoyed and learned from this article. I indeed enjoyed writing this. Feel free to let me know your thoughts about it.