paint-brush
How to Build an NFT Project with Foundry & Figment DataHubby@pnpancholi
1,964 reads
1,964 reads

How to Build an NFT Project with Foundry & Figment DataHub

by Pradhumna PancholiMay 30th, 2022
Read on Terminal Reader
Read this story w/o Javascript

Too Long; Didn't Read

Twitter is arguably the most powerful social media network on earth, even though it does not rank in the global top ten in terms of users. The site’s advanced search function allows users to search for every conceivable term, so even if you can only remember a few words of a tweet you’re looking for, Twitter will help you find it. There are many ways to use lists inside and outside the Twitter app, or you can use a browser extension like this[ ](https://www.google.com/webstore/detail/twitter-bookmarks-search/flkokionhgagpmnhlngldhbfnblmenen)

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coins Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - How to Build an NFT Project with Foundry & Figment DataHub
Pradhumna Pancholi HackerNoon profile picture


GitHub Repo:- https://github.com/PradhumnaPancholi/Figbot


Hey everyone! A little while ago, I was learning Dapp Tools 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.


Then I stumbled upon Foundry. It has everything that Dapp Tools offers apart from built-in symbolic execution (which is not a problem for me as I use Manticore by Trail of Bits ). And this is auditing related hence not a hindrance in smart contract development by any stretch of the imagination.


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:


  1. Forge: Ethereum testing framework (like Truffle, Hardhat, and Dapptools).
  2. Cast: Swiss army knife for interacting with EVM smart contracts, sending transactions, and getting chain data.
  3. Anvil: local Ethereum node, akin to Ganache, Hardhat Network


Today’s focus is going to be on Forge. But I will be posting in-depth articles on Caste and Anvil in the upcoming weeks.


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 Scupy Trooples once mentioned that they used the same approach while developing Alchemix Finance on 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 forge initonce you are inside the directory. This command will create a foundry project for you with git initialized.


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 src, tests in test, and lib contains all the libraries you installed, e.g., OpenZeppelin. In addition to that, you get foundry.toml which contains all the configurations just like hardhat.config.js and brownie-config.yaml 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.


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 Contract.sol and test/Contract.t.sol to Figbot.sol and Figbot.t.sol respectively. Now, we can not write smart contracts without Openzeppelin, can we?


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 forge build , and it will compile our project. The compiler will yell at you if there is something wrong. Otherwise, you will get a successful compile.


Managing dependencies

Just like any other package manager, Forge allows you to use forge install <lib>, forge remove <lib>, and forge update <lib> to manage your dependencies.


Let’s Complete The NFT Contract:

We will be using three contracts from the Openzeppelin. Counters, ERC721URIStorage, and Ownable. Time to upload our asset to IPFS using Pinata. We use the Ownable contract to set deploying address owner and have access to onlyOwner modifier to allow only the owner to withdraw funds. Counters to help us with token id(s) and ERC721URIStorage to keep the NFT contract simple.


  1. Setting state variable:

    1. MAX_SUPPLY to 100
    2. COST to 0.69 ether
    3. TOKEN_URI to CID, we receive from Pinata
  2. Using Counter for token id:

    1. using Counters for Counters.Counter;
    2. Counters.Counter private tokenIds;
  3. ERC721 constructor:

    1. constructor() ERC721(“Figbot”, “FBT”) {}
  4. Mint function:

    1. Check if msg.value is greater than COST
    2. Check if tokenIds.current() is greater or equal to MAX_SUPPLY
    3. Perform _safeMint and _setTokenURI
  5. Withdraw function:

    1. 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); }
  6. TotalSupply function:

    1. 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 forge test and get used to writing tests in native solidity. We will be three Foundry cheat codes (I love them!) to manage account states to fit our test scenario.


We will be testing for the following scenarios:

  1. Max Supply
  2. Successful mint
  3. Failed mint due to insufficient balance
  4. 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 vm instance, which is a part of Foundry’s Test library.


We will be using three cheatcodes in our tests :

  1. startPrank : Sets msg.sender for all subsequent calls until stopPrank is called.

  2. stopPrank:

    Stops an active prank started by startPrank, resetting msg.sender and tx.origin to the values before startPrank was called.

  3. deal : Sets the balance of an address provided address to the given balance.

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 setUp function.


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 figbot of type Figbot . This is also the place where I like to define user accounts. In Foundry, you can describe an address by using the syntax address(0x1243). you can use any four alphanumeric characters for this. I have created the accounts named owner, Alice, and bob, respectively.


Now our setUp 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 startPrank 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 withdrawFunds . Hence, we switch to the “owner” account for this deployment.


Test MaxSupply:

Starting with a simple assertion test to learn Foundry convention. By convention, all the test functions must have the prefix test . And we use assertEq to test if two values are equal.

We call our MaxSupply function and test if the result value is 100, as we described in our contract. And we use forge test to run our tests.


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.

  1. Switch user account to Alice.
  2. Set Alice’s balance to 1 ether
  3. Call the mint function
  4. Check if balanceOf Alice is 1

TestFail Mint:

We have another testing function used for tests that we expect to fail. The prefix used for such a test is testFail . We will test if the mint function reverts if the caller has insufficient funds.

  1. Switch user account to Bob
  2. Set Bob’s balance to 0.5 ether (our NFT is 0.69 ether)
  3. Call the mint function (it will be reverted due to not having enough funds)
  4. Check if balanceOf Bob is 1

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 testFail for. So when you run forge test, it will pass.

Test Withdraw:

Here we will test a function that only the “owner” can successfully perform. For this test, we will :

  1. Switch user to Bob
  2. Give Bob’s account the balance of 1 ether
  3. Mint a Figbot from Bob’s account ( this will give the contract a balance of 0.69 ether )
  4. Switch the user to the owner account
  5. Perform withdrawFunds function ( if successful, it should make the owner’s balance 0.69 ether)
  6. 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:

  1. Create an account on Figment DataHub.
  2. Click on “Create New App.”
  3. Fill in the app name.
  4. Choose “Staging” for the environment.
  5. 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 Etherscan API key for that.

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.