Back to All Episodes
Season 1Episode 25

Smart Contract Development with Foundary

June 24, 2022
35m

Listen Now

About This Episode

In this episode of DevNTell, Nader Dabit, the founder of Developer DAO, provides a comprehensive overview of smart contract development using Foundry. Foundry is a fast, Rust-based development environment for Ethereum that allows developers to write their smart contracts, tests, and deployment scripts entirely in Solidity. Nader highlights the benefits of Foundry over other tools like Hardhat, emphasizing its speed, fuzzing capabilities, and the elimination of context switching between languages. The session includes a live demonstration of initializing a project, writing and testing a Counter contract, compiling ABIs, and deploying the contract to a local Ethereum node using Anvil. Nader also showcases how to use Cast for CLI-based contract interaction and demonstrates advanced testing techniques using Forge's standard library.

Key Takeaways

1

Foundry is a modular, portable, and fast Ethereum development toolkit written in Rust, consisting of Forge, Cast, and Anvil.

2

A major advantage of Foundry is the ability to write tests and deployment scripts in Solidity, which reduces the complexity for developers who only want to work with one language.

3

Forge supports 'fuzzing,' allowing developers to run tests with arbitrary function arguments to ensure contract robustness.

4

Cast is a powerful CLI tool for interacting with smart contracts, allowing developers to make calls and send transactions directly from the terminal.

5

Anvil provides a fast, local Ethereum node environment similar to Ganache or Hardhat Network, used for local testing and development.

6

The Forge Standard Library includes helpful utilities for assertions and simulating user interactions (e.g., using vm.prank for identity simulation).

Timestamps(click to jump)

Episode Transcript

Narb

GM, GAG everybody! Welcome to what's going to be another great DevNTell. If you didn't know, DevNTell is a 30-minute window for members of the DAO to showcase something they're passionate about or have been working on. This can be an awesome project you've been working on, demonstrating unit testing best practices, automation goodies, smart contracts, how to structure a project, etc. Basically, if you've got a passion for something, this is your opportunity to share it with the community. And today I am very happy to introduce to you a man who needs no introduction: Nader Dabit, the founder of Developer DAO, who is going to be giving us an overview of smart contract development with Foundry. Take it away, Nader.

Nader Dabit

Hey, what's up? Thanks for the intro. And let me see how sharing my screen works here. So it looks like I have to share my whole screen, huh? It would be nice if I could kind of like take a portion of it because I'm using a wide monitor and it's going to be hard for people to read. So what I'm probably going to do is just give me one moment, I'm going to go ahead and attach a separate monitor to my workspace and then use that one because it's a square monitor. So just give me about 45 seconds and I'll have this up and running. Maybe it will give some more time for people to show up anyway.

Narb

No worries, no worries. All right, there we go. Awesome. Yeah, you're doing great. I can hear and see you. Yeah.

Nader Dabit

So today I actually decided to go ahead and put together and release like a cheat sheet because when I was learning Foundry, I kept needing certain references and the documentation is pretty good but it's kind of spread out. So I decided to kind of condense it into a thing. I actually kind of was creating this for myself; it was just basically a gist and I was just going and referencing it. And then I was like, 'Oh, this is actually something useful,' so I figured I'd open-source it. But before I kind of get into this, I guess I'll get an overview of what Foundry is.

Nader Dabit

So Foundry is a Solidity development environment, and it is something like you could maybe compare it to Hardhat, DappTools, Brownie, or Truffle. And it is really, really great and I'm liking it so far. I feel like it's probably my favorite Solidity development environment now, whereas Hardhat was only maybe a few months ago. And it's funded in a sense by Paradigm, which is a venture capital firm, and it's worked on by probably two of the best engineers in Web3. One of them is Georgios Konstantopoulos and then Matthias Seitz. But anyway, so that's just kind of giving an overview of where this came from.

Nader Dabit

Some of the main differences between Foundry and Hardhat or any of these other ones are that with Foundry, you have the ability to write your tests and your scripts in Solidity. So instead of context switching between Python or JavaScript and Solidity, you can write your smart contracts in Solidity and then write your tests and your scripts in Solidity. It also has something called fuzzing, which allows you to create test functions that essentially create arbitrary function arguments, so you don't actually have to define the arguments. It'll kind of allow you to do more robust testing where you can kind of just create the test and it will populate the arguments for you. And then it's really fast. And then finally, it has a tool called Cast, which is a really great CLI tool for interacting with live contracts that have been deployed to test or live networks.

Nader Dabit

So in this presentation, what I want to do is go ahead and just show you how to build out a smart contract, test it out, and deploy it using Foundry. To do that, what I want to do is go ahead and make this a lot bigger first. And is that pretty much legible by everyone?

Narb

Yeah, looks legible to me. OK, cool.

Nader Dabit

So with Foundry, you have three separate tools. You have Forge, which is a CLI that allows you to create projects, run scripts, and run tests. You have Cast, which is a CLI that allows you to make contract calls from your command line. You have Anvil, which spins up a local Ethereum network or a local Ethereum node, similar to if you ran npx hardhat node or Ganache. And then you also have the Forge Standard Library. The Forge Standard Library is a set of smart contracts that allows you to run things like assertions as well as other different simulations within a contract.

Nader Dabit

So what I'm going to start with is Forge. If you want to install this, you would just go to the Foundry GitHub and look for the installation and run this curl command. And then after you run the curl command, you'll have forge, cast, and anvil. These three are the things that we're going to be working with. So with that being said, we're going to go ahead and spin up a new project by running forge init and then the name of the repo. So I'll say foundry-example-project.

Nader Dabit

And here you kind of have your basic project. It's kind of like npx hardhat init where you get an example codebase to start off with. I'll go ahead and open that up in my text editor. With Foundry, the boilerplate that you're given has a test example, it has a script example, and it has a contract that's really not even an example; it's just a very bare empty contract.

Nader Dabit

So for the contract, we want to just create like a basic smart contract. The example that I've been using is like a counter example. So what we can do is create a basic contract here. And here we might take like an argument of count and then we can set the count to equal count. We'll make that private and then we'll have a function that returns it. So for counter, we'll have a function for incrementing the counter, for decrementing the counter, and then we'll have a function getCount for just returning the count.

Nader Dabit

I think just because you have to say uint256. There we go. OK, so for increment, we'll just put count += 1 and decrement count -= 1. Really basic stuff here. And this is kind of going to be a good starting point for us.

Nader Dabit

The first thing you might consider you might need from this contract is your ABIs if you wanted to build out an application. So you can actually go ahead and compile ABIs by running forge build. And then in this out directory, you should have contract.json and here you'll see that we have the constructor, we have decrement, getCount, and all these different functions available.

Nader Dabit

And the next thing we might want to do is run some tests. So for tests, we want to go to contract.t.sol. Now before we actually go into the test, you'll notice that we're importing forge-std/Test.sol. We have a lib folder and you can actually like install and uninstall different dependencies. And with Forge comes the Forge Standard Library. You'll notice that we're actually not defining the path for this because where we are right now is here. So we really should be saying something like import lib/forge-std/src/Test.sol or something like that.

Nader Dabit

Anyway, so this would be the exact import, but we don't really have to actually do all that. What we're basically doing here is just like assuming that our text editor and our environment knows where that is. The way that you would set that up is by setting remappings. You can kind of get an idea around what remappings you might need just by running forge remappings here. And here we're having the ds-test, which is an assertion library mainly, and then the Forge Standard Library and it tells you kind of where these are located.

Nader Dabit

So to set all this up, I guess the main thing I'm about to try to get to is that there's a Solidity extension and I've installed that. And then once you have that installed, you can go to the settings and look for Solidity and you can actually set up the remappings here by defining your default dependencies contracts directory as src and the dependencies directory as lib. So that means when I import from lib, it'll have to define like the exact path it's going up and getting that.

Nader Dabit

So once you have like your remappings and your text editor configuration stuff set up, what you can do is then just go ahead and create your test. So here in the setup, we can create a function that essentially just gets run before every function. I'm in the wrong... let me go into the test folder. So in the setUp function, you can create a function that gets called every time. So what we might do here is say Counter counter and then here we need to go ahead and import Counter.

Nader Dabit

Actually we can just import the contract, I'm just renaming it to Counter. All right, so now we're importing the contract and we're defining a counter. And here we can say counter = new Counter() and pass in the value that we'd like to start with. And then here we can now start like using the assertions. So the first thing we might want to do is to get the count.

Nader Dabit

I guess we can expect the counter to be 10, so we can assertEq by passing the value and 10. And this should be like about the most basic test that you can run. So here if we run forge test, then we'll see that that's successful. And if we set this to like 11 or something like that, then it would become unsuccessful.

Nader Dabit

We might then test the increment function, so we can say function testIncrement(). And counter.incrementCounter(); and then here if we set this to 11, then that should pass because we start off at 10, we incremented once, and now we're at 11. So those two pass.

Nader Dabit

So that's kind of some basic stuff there. What we might want to do now is we're going to deploy this contract to a local node and then we want to create a more complex contract where we can emulate users and do a little bit more like fun stuff. To deploy this, like you might have been used to running npx hardhat node and getting the node. Well, there's also Anvil with Foundry.

Nader Dabit

If you run anvil, then you'll see that Anvil just goes ahead and spins up your local network. And you get like 10 accounts and you get 10 private keys. You could also say anvil --help and you'll see that you can pass in all types of stuff.

Nader Dabit

But with that node running, we can now deploy our contract. And the way that we're going to do that is by going to the contract script. We're not going to really use the setUp function, I don't believe. What we want to do though is just go ahead and deploy this thing. So to do that, we can go ahead and use vm.broadcast(). Whoever is executing this contract is going to be the user that is deploying it. So for our example, we're just going to be calling it from the command line, so we're going to be passing in a private key.

Nader Dabit

So if we call vm.broadcast(), what we can do now here is we want to go ahead and import the contract. And then now what we can do is just call new Counter() and pass in the constructor. That's really all you've got to do. This will go ahead and deploy the contract. Now if we didn't call broadcast, it wouldn't actually deploy this, so that's interesting.

Nader Dabit

So to run this, all we need to do is use the command line and use forge script scripts/Contract.s.sol:ContractScript --fork-url http://localhost:8545 --private-key [KEY] --broadcast. And this deploys the contract and it gives you like a message saying complete and successful and it even writes a lot of information to your local environment.

Nader Dabit

But we also see that we have the contract address logged here. And what we can now do is use Cast to make calls against it. So I can say cast call [ADDRESS] "getCount()". And then here we see that 10 was returned. And then we might next want to send a transaction. So I'm going to go ahead and save this private key in an environment variable. Cast send [ADDRESS] "incrementCounter()" --private-key $PRIVATE_KEY. And then now if we call cast call again, then you'll see that we have 11.

Nader Dabit

So from Cast, we can kind of, you know, make calls and we can do this for really any contract in the entire world. The last thing we'll do is go to Solidity by Example and grab the ERC721 and we'll mock a user in the test. This will be a good way to kind of show some of the other stuff that you can do. The first thing we might want to do is mint a token. So we'll say function testTransfer(). We want to simulate Bob, and the way we can do that is we can say vm.startPrank(bob); and then ERC721.safeTransferFrom(bob, mary, 1); and then we can assertEq(ERC721.ownerOf(1), mary);.

Nader Dabit

And this should allow us to transfer. If we didn't have this prank here, then we're now trying to transfer a token that we don't own and this should fail. It says 'not owner nor approved.' Nice. And that's about it. But I would say if you want to learn more about Foundry, I kind of compiled all my favorite information into this Foundry Cheat Sheet. All that stuff is linked there. So yeah, thanks for coming and checking this out.

Narb

Awesome, awesome. Thank you so much for coming on Nader and giving us that awesome demo. It'd be nice to actually stay with one language when doing all the Solidity development, so I'm definitely going to be checking it out. I want to wish everybody a very happy Friday and or weekend, wherever it is for you. I'll be dropping a link for the attendees to claim their POAP and Nader I'll send you your speaker POAP through DM. But with that, we'll stop the recording.

Listen On

Share This Episode

Share on X

Watch Episodes Live!

Subscribe to our event calendar and never miss a live episode.

View Event Calendar