diff --git a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/1-erc20-welcome-to-advanced/+page.md b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/1-erc20-welcome-to-advanced/+page.md
index 6a555bcfb..a7239cf3c 100644
--- a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/1-erc20-welcome-to-advanced/+page.md
+++ b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/1-erc20-welcome-to-advanced/+page.md
@@ -13,7 +13,7 @@ If you have any questions or just want to join the discussion, you can do it at
---
[Discord](https://discord.gg/cyfrin)
---
-[GitHub](https://github.com/Cyfrin/path-solidity-developer-2023/discussions)
+[GitHub](https://github.com/Cyfrin/foundry-full-course-cu/discussions)
---
-See you in the next lesson!
\ No newline at end of file
+See you in the next lesson!
diff --git a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/2-erc20-basics/+page.md b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/2-erc20-basics/+page.md
index 1517d39b7..728fc1f7c 100644
--- a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/2-erc20-basics/+page.md
+++ b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/2-erc20-basics/+page.md
@@ -10,7 +10,7 @@ _Follow along the course with this video._
Welcome back! I hope you're ready to move into some more advanced concepts in Web3. In this section we're going to be diving into `ERC20s`, and how to develop, deploy and test them.
-You can find the code associated with section in the [**GitHub repo**](https://github.com/Cyfrin/foundry-full-course-f23) associated with this course, and in the [**section repo specifically**](https://github.com/Cyfrin/foundry-erc20-f23).
+You can find the code associated with section in the [**GitHub repo**](https://github.com/Cyfrin/foundry-full-course-cu) associated with this course, and in the [**section repo specifically**](https://github.com/Cyfrin/foundry-erc20-cu).
Before we rush into creating our own `ERC20 Token`, let's take a moment to learn _what_ an `ERC20` is, what `EIPs` are and where all these acronyms mean.
@@ -20,11 +20,24 @@ The Web3 and blockchain ecosystem is fundamentally democratic and open source. A
If `EIPs` get enough traction to warrant genuine consideration they will often generate a `Request for Comments`, in Ethereum these are known as `Ethereum Request for Comments` (`ERCs`).
-> ❗ **NOTE** > `EIPs` and `ERCs` are numbered chronologically! `ERC20` is the 20th request for comments that was created.
+> ❗ **NOTE**
+> `EIPs` and `ERCs` are numbered chronologically! `ERC20` is the 20th request for comments that was created.
New `Improvement Proposals` and `Requests for Comments` are tracked on websites such as [**eips.ethereum.org**](https://eips.ethereum.org/), where you can watch these proposals go through the process real time and be adopted or rejected by the community.
-::image{src='/foundry-erc20s/1-erc20-basics/erc20-basics1.png' style='width: 100%; height: auto;'}
+::image{src='/foundry-erc20s/1-erc20-basics/erc20-basics1.PNG' style='width: 100%; height: auto;'}
+
+### EIP status terms
+
+- **Idea** - An idea that is pre-draft. This is not tracked within the EIP Repository.
+- **Draft** - The first formally tracked stage of an EIP in development. An EIP is merged by an EIP Editor into the EIP repository when properly formatted.
+- **Review** - An EIP Author marks an EIP as ready for and requesting Peer Review.
+- **Last Call** - This is the final review window for an EIP before moving to FINAL. An EIP editor will assign Last Call status and set a review end date (`last-call-deadline`), typically 14 days later. If this period results in necessary normative changes it will revert the EIP to Review.
+- **Final** - This EIP represents the final standard. A Final EIP exists in a state of finality and should only be updated to correct errata and add non-normative clarifications.
+- **Stagnant** - Any EIP in Draft or Review if inactive for a period of 6 months or greater is moved to Stagnant. An EIP may be resurrected from this state by Authors or EIP Editors through moving it back to Draft.
+- **Withdrawn** - The EIP Author(s) have withdrawn the proposed EIP. This state has finality and can no longer be resurrected using this EIP number. If the idea is pursued at a later date it is considered a new proposal.
+- **Living** - A special status for EIPs that are designed to be continually updated and not reach a state of finality. This includes most notably EIP-1.
+
### ERC20
@@ -34,7 +47,7 @@ These tokens essentially exists as records of value within smart contracts on ch
**_Why make an ERC20?_**
-There are a few common reasons that someone may choose to launch an `ERC20 token`, but there's very little limit to the possibilities of their application in a digital space. A few common usecases include:
+There are a few common reasons that someone may choose to launch an `ERC20 token`, but there's very little limit to the possibilities of their application in a digital space. A few common use cases include:
- Governance Tokens
- Securing an underlying network
diff --git a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/3-erc20-manual-creation/+page.md b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/3-erc20-manual-creation/+page.md
index eb570e735..919f13055 100644
--- a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/3-erc20-manual-creation/+page.md
+++ b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/3-erc20-manual-creation/+page.md
@@ -8,15 +8,15 @@ _Follow along the course with this video._
### ERC20 Manual Creation
-Welcome Back! Having covered the basics, let's look at how we can manually create our own ERC20 token. This is going to be one of our fastest lessons yet!
+Welcome back! Having covered the basics, let's look at how we can manually create our own ERC20 token. This is going to be one of our fastest lessons yet!
### Setting Up Your Environment
Open a terminal in Visual Studio Code and run the following:
```sh
-mkdir foundry-erc20-f23
-cd foundry-erc20-f23
+mkdir foundry-erc20
+cd foundry-erc20
code .
```
@@ -38,18 +38,18 @@ I'm going to show you 2 different ways to create our own token, first the hard w
You can begin by creating a new token token our `src` directory named `ManualToken.sol`. We can start this contract the same way we've been practicing this whole time (you'll get really familiar with this bit).
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
-contract ManualToken {...}
+contract ManualToken {}
```
Now, as we covered in the last lesson, all we need to do to make our token compatible is follow the [**ERC20 Token Standard**](https://eips.ethereum.org/EIPS/eip-20). Essentially this means we need to include the required functions/methods for our deployment to follow this standard. Let's add thing functionality then!
Let's start with name, decimals and totalSupply
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -59,7 +59,7 @@ contract ManualToken {
return "Manual Token";
}
- function totalSupply() public pure returns (uint256){
+ function totalSupply() public pure returns (uint256) {
return 100 ether; // 100000000000000000000
}
@@ -72,9 +72,9 @@ contract ManualToken {
> ❗ **NOTE**
> Despite being an optional method, we're including `decimals` here as a point of clarification since we're declaring our total supply as 100 ether. 100 ether = 100 + 18 decimals places.
-The next functions required by the ERC20 standard are balanceOf and transfer, so let's apply those now.
+The next functions required by the ERC20 standard are `balanceOf` and `transfer`, so let's apply those now.
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -100,13 +100,13 @@ contract ManualToken {
Hmm, what is this function meant to return exactly? We're probably going to need a mapping to track the balances of each address...
-```js
+```solidity
mapping(address => uint256) private s_balances;
```
-So now our balanceOf function can return this mapped value based on the address parameter being passed.
+So now our `balanceOf` function can return this mapped value based on the address parameter being passed.
-```js
+```solidity
function balanceOf(address _owner) public pure returns (uint256) {
return balances[_owner];
}
@@ -119,13 +119,13 @@ An interesting thing that comes to light from this function is - someone's balan
Our next required function is transfer:
-```js
+```solidity
function transfer(address _to, uint256 _amount) public {
uint256 previousBalance = balanceOf(msg.sender) + balanceOf(_to);
s_balance[msg.sender] -= _amount;
s_balance[_to] += _amount;
- require(balanceOf(msg.sender) + balanceOf(_to) == previousBalances);
+ require(s_balance(msg.sender) + s_balance(_to) == previousBalance);
}
```
@@ -136,6 +136,6 @@ So, a basic transfer function could look something like the above, a simple adju
We could absolutely continue going through each of the required functions outlined in the [**ERC20 Token Standard**](https://eips.ethereum.org/EIPS/eip-20) and eventually come out the other side with a compatible contract, but there's an easier way.
-In the next lesson we'll investigate an even easier method to spin up a standard ERC20 protocol, with the help of OpenZepplin.
+In the next lesson, we'll investigate an even easier method to spin up a standard ERC20 protocol, with the help of OpenZeppelin.
See you there!
diff --git a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/4-erc20-open-zeppelin/+page.md b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/4-erc20-open-zeppelin/+page.md
index 30435fb48..144650aa5 100644
--- a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/4-erc20-open-zeppelin/+page.md
+++ b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/4-erc20-open-zeppelin/+page.md
@@ -13,13 +13,13 @@ Welcome back! As mentioned in the closing of our last lesson, we could absolutel
In this section, I'll guide you on using the OpenZeppelin Library to achieve this.
> ❗ **NOTE**
-> OpenZeppelin is renowned for its Smart Contract framework, offering a vast repository of audited contracts readily integratable into your codebase.
+> OpenZeppelin is renowned for its Smart Contract framework, offering a vast repository of audited contracts readily integrable into your codebase.
-Access [OpenZeppelin's documentation](https://docs.openzeppelin.com/contracts/4.x/) via their official website. By navigating to [Products -> Contracts](https://www.openzeppelin.com/contracts), you can discover a vast array of ready-to-use contracts.
+Access [OpenZeppelin's documentation](https://docs.openzeppelin.com/contracts/5.x/) via their official website. By navigating to [Products -> Contracts Library](https://www.openzeppelin.com/contracts), you can discover a vast array of ready-to-use contracts.
Additionally, OpenZeppelin offers a contract wizard, streamlining the contract creation process — perfect for tokens, governances, or custom contracts.
-::image{src='/foundry-erc20s/3-erc20-open-zeppelin/ERC20-open-zeppelin1.PNG' style='width: 100%; height: auto;'}
+::image{src='/foundry-erc20s/3-erc20-open-zeppelin/erc20-open-zeppelin1.PNG' style='width: 100%; height: auto;'}
Let's leverage OpenZeppelin to create a new ERC20 Token. Create a new file within `src` named `OurToken.sol`. Once that's done, let's install the OpenZeppelin library into our contract.
@@ -35,7 +35,7 @@ remappings = ["@openzeppelin=lib/openzeppelin-contracts"]
We can now import and inherit this contract into `OurToken.sol`!
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -53,14 +53,14 @@ By importing the OpenZeppelin implementation of ERC20 this way, we inherit all t
Now, we should recall that when inheriting from a contract with a constructor, our contract must fulfill the requirements of that constructor. We'll need to define details like a name and symbol for OurToken.
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract OurToken is ERC20 {
- constructor(uint256 initialSupply) ERC20("OurToken", "OT"){
+ constructor(uint256 initialSupply) ERC20("OurToken", "OT") {
_mint(msg.sender, initialSupply);
}
}
@@ -70,7 +70,7 @@ For the purposes of simple examples like this, I like to mint the initialSupply
As always we can perform a sanity check to assure things are working as expected by running `forge build`.
-::image{src='/foundry-erc20s/3-erc20-open-zeppelin/ERC20-open-zeppelin2.PNG' style='width: 100%; height: auto;'}
+::image{src='/foundry-erc20s/3-erc20-open-zeppelin/erc20-open-zeppelin2.PNG' style='width: 100%; height: auto;'}
Nailed it.
diff --git a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/5-erc20-deploy-script/+page.md b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/5-erc20-deploy-script/+page.md
index aac6c050a..5096e71dc 100644
--- a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/5-erc20-deploy-script/+page.md
+++ b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/5-erc20-deploy-script/+page.md
@@ -16,7 +16,7 @@ We expect OurToken to behave the same, regardless of the chain it's deployed on,
To begin, we can import Script and OurToken as well as add the skeleton of our run function:
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -24,14 +24,14 @@ pragma solidity ^0.8.18;
import {Script} from "forge-std/Script.sol";
import {OurToken} from "../src/OurToken.sol";
-contract DeployOurToken is Script returns (OurToken){
+contract DeployOurToken is Script {
function run() external {}
}
```
We're going to keep this really basic, we just want to deploy OurToken. We know that OurToken requires an initial supply as a constructor parameter, so let's declare that and then deploy our contract.
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -55,10 +55,10 @@ contract DeployOurToken is Script {
Our deploy script looks great! To make things a little easier on ourselves when using the CLI to run this script, copy the Makefile from the course GitHub repo and add this to our workspace (I've included it below to copy if needed).
-
-Makefile
-```
+### Makefile
+
+```make
-include .env
.PHONY: all test clean deploy fund help install snapshot format anvil
@@ -110,15 +110,15 @@ verify:
```
-
Now, by running `make anvil` (open a new terminal once your chain has started!) followed by `make deploy`...
::image{src='/foundry-erc20s/4-erc20-deploy-script/erc20-deploy-script1.png' style='width: 100%; height: auto;'}
+
### Wrap Up
Woo! Deployment to our anvil chain successful, let's go!
-In the next lesson we'll test our contracts with the help of some AI tools and recap everything we've gone over so far. See you there!
+In the next lesson, we'll test our contracts with the help of some AI tools and recap everything we've gone over so far. See you there!
diff --git a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/6-erc20-ai-tests-and-recap/+page.md b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/6-erc20-ai-tests-and-recap/+page.md
index 4fa8a3434..a62d9412f 100644
--- a/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/6-erc20-ai-tests-and-recap/+page.md
+++ b/courses/advanced-foundry/1-How-to-create-an-erc20-crypto-currency/6-erc20-ai-tests-and-recap/+page.md
@@ -8,14 +8,14 @@ _Follow along the course with this video._
### AI Tests and Recap
-Almost done, you're doing great! The last thing we want to assure is that we're always testing any code we write before it's deployed to a production envirvonment. Fortunately, AI is becoming more and more capable each day at being able to assist us with some basic tests.
+Almost done, you're doing great! The last thing we want to assure is that we're always testing any code we write before it's deployed to a production environment. Fortunately, AI is becoming more and more capable each day at being able to assist us with some basic tests.
> ❗ **IMPORTANT**
> I want you to use AI to jumpstart your learning, don't use it to substitute learning. It can be really easy to fall back on having answers provided to us. I strongly encourage you to use AI consciously and with the intent of supporting your efforts, not doing the work for you.
We're going to write a couple tests together, then see if an AI can help us with some others. Go ahead and create a new file within our `test` folder named `OurTokenTest.t.sol`.
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -31,7 +31,7 @@ contract OurTokenTest is Test {
With this boiler plate set up in `OurTokenTest.t.sol` we can begin by declaring `OurToken` and `Deployer` variables and deploying these. We'll also need to create some accounts/addresses for our tests.
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -56,7 +56,7 @@ contract OurTokenTest is Test {
The last thing we need in our `setUp` function is to assure one of our accounts is given some `OurToken` to play with. We wrote `OurToken` to mint it's `INITIAL_SUPPLY` to the `msg.sender`. Let's have our `msg.sender` contract transfer `100 ether` worth of `OurToken` to Bob.
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -84,9 +84,9 @@ contract OurTokenTest is Test {
}
```
-Bam! With this we're ready to start writing our first test. We'll start with a simple one, let's assure that the STARTING_BALANCE was in fact sent to Bob.
+Bam! With this we're ready to start writing our first test. We'll start with a simple one, let's assure that the `STARTING_BALANCE` was in fact sent to Bob.
-```js
+```solidity
function testBobBalance() public view {
assertEq(STARTING_BALANCE, ourToken.balanceOf(bob));
}
@@ -124,11 +124,11 @@ While it costs a little gas, it's good practice to regularly assess your approva
With all this context in mind, let's look at what a test for OurToken allowances looks like.
-```js
+```solidity
function testAllowancesWork() public {
uint256 initialAllowance = 1000;
- //Bob approves Alice to spend 1000 tokens.
+ // Bob approves Alice to spend 1000 tokens.
vm.prank(bob);
ourToken.approve(alice, initialAllowance);
@@ -141,14 +141,14 @@ function testAllowancesWork() public {
Here, we're declaring an initial balance and pranking Bob to call approve on `OurToken`. This is allowing `Alice` to transfer up to `1000 OurTokens`.
-We then declare a transfer amount, and prank `Alice` as we call `transferFrom`, transfering tokens from `Bob`'s address to `Alice`'s.
+We then declare a transfer amount, and prank `Alice` as we call `transferFrom`, transferring tokens from `Bob`'s address to `Alice`'s.
> ❗ **NOTE**
> The `transfer` function won't work here as the `from` address defaults to msg.sender!
All we need now is our assert statements.
-```js
+```solidity
assertEq(outToken.balanceOf(alice), transferAmount);
assertEq(ourToken.balanceOf(bob), STARTING_BALANCE - transferAmount);
```
@@ -173,10 +173,9 @@ AI can be a little hit and miss when it comes to accuracy and reliability, but o
OpenAI has a very capable free tier for ChatGPT available [**here**](https://chatgpt.com). Navigate to the platform and create an account if needed.
-
-AI Prompt
+### AI Prompt
-```
+````text
Here is my solidity ERC20 token.
```
@@ -239,17 +238,16 @@ Can you write the rest of the tests? Please include tests for:
- Allowances
- transfers
- anything else that might be important
-```
+````
-
> ❗ **NOTE**
> Your specific response will differ from mine, I encourage you to try using an AI tool like ChatGPT to generate a few tests of your own. I've included my AI response below, generated by GPT4o May 31, 2024.
-
-Example AI Response
+### Example AI Response
+````text
Sure, here are the rest of the tests for your ERC20 token, including tests for allowances, transfers, and a few other important functionalities.
```solidity
@@ -356,8 +354,7 @@ contract OurTokenTest is StdCheats, Test {
- **testFailApproveExceedsBalance**: Ensures that approving more than the balance fails.
- **testTransferEvent**: Verifies that the `Transfer` event is emitted correctly during a transfer.
- **testApprovalEvent**: Verifies that the `Approval` event is emitted correctly during an approval.
-
-
+````
I'll caution you against blindly copying and pasting AI responses and to use what's generated intelligently. Some things will make more sense for your situation that others.
@@ -376,10 +373,9 @@ See you soon!
---
-
-AI Tests
+### AI Tests
-```js
+```solidity
function testTransfer() public {
uint256 amount = 1000 * 10 ** 18; // Example amount
vm.prank(msg.sender);
@@ -414,5 +410,3 @@ function testFailApproveExceedsBalance() public {
ourToken.approve(user1, amount); // This should fail
}
```
-
-
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/1-nfts/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/1-nfts/+page.md
index 3ade0608f..31bd60a36 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/1-nfts/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/1-nfts/+page.md
@@ -11,7 +11,7 @@ _Follow along the course with this video._
Welcome back! In this section of the course we'll be investigate Non-fungible Tokens (NFTs), we'll learn what an NFT is, why they're so cool and how to create our very own NFTs, one basic and one advanced.
> ❗ **PROTIP**
-> All the code discussed in this section is available in the associated [**GitHub Repo**](https://github.com/Cyfrin/foundry-nft-f23).
+> All the code discussed in this section is available in the associated [**GitHub Repo**](https://github.com/Cyfrin/foundry-nft-cu).
### Two Flavors
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/12-svg-nft/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/12-svg-nft/+page.md
index 932005775..4f6c2e2af 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/12-svg-nft/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/12-svg-nft/+page.md
@@ -28,7 +28,7 @@ contract MoodNft is ERC721 {
}
```
-Looking good! We want to store the `SVG` art on chain, we we're actually going to pass these to our `constructor` on deployment.
+Looking good! We want to store the `SVG` art on chain, we're actually going to pass these to our `constructor` on deployment.
```js
constructor(string memory sadSvg, string memory happySvg) ERC721("Mood NFT", "MN"){}
@@ -73,6 +73,6 @@ function tokenURI(uint256 tokenId) public view override returns (string memory){
### Wrap Up
-Our on-chain, dynamic, `SVG NFT` is slowly coming to life! In the next lesson let's walk through the contents of our `tokenURI` function and how we can encode our `SVGs` in a way such that they can be reasonably stored on the blockchain.
+Our on-chain, dynamic, `SVG NFT` is slowly coming to life! In the next lesson, let's walk through the contents of our `tokenURI` function and how we can encode our `SVGs` in a way such that they can be reasonably stored on the blockchain.
See you there!
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/13-svg-nft-encoding/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/13-svg-nft-encoding/+page.md
index b03d4f8f6..b255996a9 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/13-svg-nft-encoding/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/13-svg-nft-encoding/+page.md
@@ -38,7 +38,8 @@ contract MoodNft is ERC721 {
}
```
-> ❗ **IMPORTANT** >**tokenURI != imageURI**
+> ❗ **IMPORTANT**
+>**tokenURI != imageURI**
>
> It's important to remember that imageURI is one property of a token's tokenURI. A tokenURI is usually a JSON object!
@@ -160,8 +161,7 @@ function _baseURI() internal pure override returns(string memory){
Now, in our tokenURI function again, we can concatenate the result of this \_baseURI function with the Base64 encoding of our JSON object... and finally we can type cast all of this as a string to be returned by our tokenURI function.
```js
-return;
-string(
+return string(
abi.encodePacked(
_baseURI(),
Base64.encode(
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/19-advanced-evm/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/19-advanced-evm/+page.md
index 606cdb23c..bdfb78462 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/19-advanced-evm/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/19-advanced-evm/+page.md
@@ -41,7 +41,7 @@ Now, if we deploy this and call our combineStrings function, our output is `Hi M
::image{src='/foundry-nfts/19-advanced-evm/advanced-evm1.png' style='width: 100%; height: auto;'}
-What our function is ultimtely doing is encoding `Hi Mom! ` and `Miss you!` into its bytes form and then casting these bytes into a string.
+What our function is ultimately doing is encoding `Hi Mom! ` and `Miss you!` into its bytes form and then casting these bytes into a string.
If we just run abi.encodePacked without converting to a string we get:
@@ -54,7 +54,7 @@ bytes: 0x4869204d6f6d21204d69737320796f7521
You'll see lots of things throughout this list that you're already familiar with. Things like `msg.sender`, `msg.value`, `block.chainid` and more. I encourage you to look through the list!
> ❗ **NOTE**
-> Since Solidity v0.8.12, you no longer need to use abi.encodePacked to contenate strings. The preferred method is via `string.concat(stringA, stringB)`.
+> Since Solidity v0.8.12, you no longer need to use abi.encodePacked to concatenate strings. The preferred method is via `string.concat(stringA, stringB)`.
Before we dive deeper into what's happening when we call `encodePacked`, let's first investigate some of the finer details of sending a transaction.
@@ -80,16 +80,12 @@ We can see this in Etherscan for any contract we've deployed. Here's an [**examp
### Op Codes
-
-Bytecode Example
+Bytecode Example
```
0x60806040523480156200001157600080fd5b506040518060400160405280600881526020016710985cda58d3919560c21b8152506040518060400160405280600381526020016210919560ea1b815250816000908162000060919062000124565b5060016200006f828262000124565b5050600060065550620001f0565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620000a857607f821691505b602082108103620000c957634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200011f576000816000526020600020601f850160051c81016020861015620000fa5750805b601f850160051c820191505b818110156200011b5782815560010162000106565b5050505b505050565b81516001600160401b038111156200014057620001406200007d565b620001588162000151845462000093565b84620000cf565b602080601f831160018114620001905760008415620001775750858301515b600019600386901b1c1916600185901b1785556200011b565b600085815260208120601f198616915b82811015620001c157888601518255948401946001909101908401620001a0565b5085821015620001e05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61133f80620002006000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806370a082311161008c578063b88d4fde11610066578063b88d4fde146101e1578063c87b56dd146101f4578063e985e9c514610207578063fb37e8831461021a57600080fd5b806370a08231146101a557806395d89b41146101c6578063a22cb465146101ce57600080fd5b8063095ea7b3116100c8578063095ea7b31461015757806323b872dd1461016c57806342842e0e1461017f5780636352211e1461019257600080fd5b806301ffc9a7146100ef57806306fdde0314610117578063081812fc1461012c575b600080fd5b6101026100fd366004610d6d565b61022d565b60405190151581526020015b60405180910390f35b61011f61027f565b60405161010e9190610dd7565b61013f61013a366004610dea565b610311565b6040516001600160a01b03909116815260200161010e565b61016a610165366004610e1f565b610338565b005b61016a61017a366004610e49565b610452565b61016a61018d366004610e49565b610483565b61013f6101a0366004610dea565b61049e565b6101b86101b3366004610e85565b6104fe565b60405190815260200161010e565b61011f610584565b61016a6101dc366004610ea0565b610593565b61016a6101ef366004610f68565b6105a2565b61011f610202366004610dea565b6105da565b610102610215366004610fe4565b61067c565b6101b8610228366004611017565b6106aa565b60006001600160e01b031982166380ac58cd60e01b148061025e57506001600160e01b03198216635b5e139f60e01b145b8061027957506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606000805461028e90611060565b80601f01602080910402602001604051908101604052809291908181526020018280546102ba90611060565b80156103075780601f106102dc57610100808354040283529160200191610307565b820191906000526020600020905b8154815290600101906020018083116102ea57829003601f168201915b5050505050905090565b600061031c826106ea565b506000908152600460205260409020546001600160a01b031690565b60006103438261049e565b9050806001600160a01b0316836001600160a01b0316036103b55760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b03821614806103d157506103d1813361067c565b6104435760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c00000060648201526084016103ac565b61044d838361074c565b505050565b61045c33826107ba565b6104785760405162461bcd60e51b81526004016103ac9061109a565b61044d838383610819565b61044d838383604051806020016040528060008152506105a2565b6000818152600260205260408120546001600160a01b0316806102795760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b60448201526064016103ac565b60006001600160a01b0382166105685760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b60648201526084016103ac565b506001600160a01b031660009081526003602052604090205490565b60606001805461028e90611060565b61059e33838361097d565b5050565b6105ac33836107ba565b6105c85760405162461bcd60e51b81526004016103ac9061109a565b6105d484848484610a4b565b50505050565b60008181526007602052604090208054606091906105f790611060565b80601f016020809104026020016040519081016040528092919081815260200182805461062390611060565b80156106705780601f1061064557610100808354040283529160200191610670565b820191906000526020600020905b81548152906001019060200180831161065357829003601f168201915b50505050509050919050565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b60065460008181526007602052604081209091906106c88482611137565b506106d33382610a7e565b6006546106e19060016111f7565b60065592915050565b6000818152600260205260409020546001600160a01b03166107495760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b60448201526064016103ac565b50565b600081815260046020526040902080546001600160a01b0319166001600160a01b03841690811790915581906107818261049e565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806107c68361049e565b9050806001600160a01b0316846001600160a01b031614806107ed57506107ed818561067c565b806108115750836001600160a01b031661080684610311565b6001600160a01b0316145b949350505050565b826001600160a01b031661082c8261049e565b6001600160a01b0316146108525760405162461bcd60e51b81526004016103ac90611218565b6001600160a01b0382166108b45760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b60648201526084016103ac565b826001600160a01b03166108c78261049e565b6001600160a01b0316146108ed5760405162461bcd60e51b81526004016103ac90611218565b600081815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260038552838620805460001901905590871680865283862080546001019055868652600290945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b816001600160a01b0316836001600160a01b0316036109de5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c65720000000000000060448201526064016103ac565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b610a56848484610819565b610a6284848484610a98565b6105d45760405162461bcd60e51b81526004016103ac9061125d565b61059e828260405180602001604052806000815250610b99565b60006001600160a01b0384163b15610b8e57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290610adc9033908990889088906004016112af565b6020604051808303816000875af1925050508015610b17575060408051601f3d908101601f19168201909252610b14918101906112ec565b60015b610b74573d808015610b45576040519150601f19603f3d011682016040523d82523d6000602084013e610b4a565b606091505b508051600003610b6c5760405162461bcd60e51b81526004016103ac9061125d565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610811565b506001949350505050565b610ba38383610bcc565b610bb06000848484610a98565b61044d5760405162461bcd60e51b81526004016103ac9061125d565b6001600160a01b038216610c225760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f206164647265737360448201526064016103ac565b6000818152600260205260409020546001600160a01b031615610c875760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016103ac565b6000818152600260205260409020546001600160a01b031615610cec5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e7465640000000060448201526064016103ac565b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6001600160e01b03198116811461074957600080fd5b600060208284031215610d7f57600080fd5b8135610d8a81610d57565b9392505050565b6000815180845260005b81811015610db757602081850181015186830182015201610d9b565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610d8a6020830184610d91565b600060208284031215610dfc57600080fd5b5035919050565b80356001600160a01b0381168114610e1a57600080fd5b919050565b60008060408385031215610e3257600080fd5b610e3b83610e03565b946020939093013593505050565b600080600060608486031215610e5e57600080fd5b610e6784610e03565b9250610e7560208501610e03565b9150604084013590509250925092565b600060208284031215610e9757600080fd5b610d8a82610e03565b60008060408385031215610eb357600080fd5b610ebc83610e03565b915060208301358015158114610ed157600080fd5b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115610f0d57610f0d610edc565b604051601f8501601f19908116603f01168101908282118183101715610f3557610f35610edc565b81604052809350858152868686011115610f4e57600080fd5b858560208301376000602087830101525050509392505050565b60008060008060808587031215610f7e57600080fd5b610f8785610e03565b9350610f9560208601610e03565b925060408501359150606085013567ffffffffffffffff811115610fb857600080fd5b8501601f81018713610fc957600080fd5b610fd887823560208401610ef2565b91505092959194509250565b60008060408385031215610ff757600080fd5b61100083610e03565b915061100e60208401610e03565b90509250929050565b60006020828403121561102957600080fd5b813567ffffffffffffffff81111561104057600080fd5b8201601f8101841361105157600080fd5b61081184823560208401610ef2565b600181811c9082168061107457607f821691505b60208210810361109457634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b601f82111561044d576000816000526020600020601f850160051c810160208610156111105750805b601f850160051c820191505b8181101561112f5782815560010161111c565b505050505050565b815167ffffffffffffffff81111561115157611151610edc565b6111658161115f8454611060565b846110e7565b602080601f83116001811461119a57600084156111825750858301515b600019600386901b1c1916600185901b17855561112f565b600085815260208120601f198616915b828110156111c9578886015182559484019460019091019084016111aa565b50858210156111e75787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561027957634e487b7160e01b600052601160045260246000fd5b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906112e290830184610d91565b9695505050505050565b6000602082840312156112fe57600080fd5b8151610d8a81610d5756fea264697066735822122043a0d877831374d5912a657fb0f0442ba8618c52e0dec412cde065bffa638b3564736f6c63430008180033
```
-
-
-
The above may look like random numbers and letters to us, but to the `Ethereum Virtual Machine (EVM)`, this is effectively the alphabet it uses to perform computation. Every 2 bytes in the data above actually represents an op code. The website [**evm.codes**](https://www.evm.codes/) is an amazing resource for referencing these things.
::image{src='/foundry-nfts/19-advanced-evm/advanced-evm5.png' style='width: 100%; height: auto;'}
@@ -124,7 +120,7 @@ Go ahead and compile/deploy Encoding.sol with this new function and call it. We
::image{src='/foundry-nfts/19-advanced-evm/advanced-evm7.png' style='width: 100%; height: auto;'}
-This hex formmat, this encoding, is how a computer understands the number `1`.
+This hex format, this encoding, is how a computer understands the number `1`.
Now, as mentioned, this can be used to encode basically anything, we can write a function to encode a string and see what it's output would be just the same.
@@ -257,7 +253,7 @@ This one actually _will_ work.
### Wrap Up
-Don't feel back if this doesn't click right away, we're broaching some low-level concepts and functions here.
+Don't feel bad if this doesn't click right away, we're broaching some low-level concepts and functions here.
We're making great progress though and should have at least a somewhat better understanding of how the various methods of encoding and decoding are used in the EVM.
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/20-evm-encoding/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/20-evm-encoding/+page.md
index 8a831fa4d..83e277204 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/20-evm-encoding/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/20-evm-encoding/+page.md
@@ -27,9 +27,9 @@ I've said previously that in order to send a transaction you're always going to
Originally we were referring to the human-readable ABI.
-
-Human-readable ABI
-```
+Human-readable ABI
+
+```json
[
{
"inputs": [],
@@ -73,9 +73,6 @@ Originally we were referring to the human-readable ABI.
]
```
-
-
-
We can also accomplish our goals with the `bytecode` version directly. All you _really_ need to send a function call is the name of a function and the input types.
Two questions arise:
@@ -99,7 +96,8 @@ function withdraw(address recentWinner) public {
**staticcall:** How we call view or pure functions
-> ❗ **PROTIP** > `send` and `delegatecall` also exist as options for low-level calling to the blockchain, but we'll go over these in greater detail later!
+> ❗ **PROTIP**
+> `send` and `delegatecall` also exist as options for low-level calling to the blockchain, but we'll go over these in greater detail later!
When we write `recentWinner.call{value: address(this).balance}("");` we're directly updating the value property of the transaction we're sending. The parenthesis at the end of this call are where we provide our transaction data.
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/21-evm-recap/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/21-evm-recap/+page.md
index 2a4728802..119484e8a 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/21-evm-recap/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/21-evm-recap/+page.md
@@ -12,7 +12,7 @@ Before looking at how we can apply all our new encoding knowledge to call our ow
### Concatenation
-At a high-level we learnt that abi.encodePacked can be used to concatenate strings.
+At a high-level, we learnt that abi.encodePacked can be used to concatenate strings.
```js
string memory someString = string(abi.encodePacked("Hi Mom! ", "Miss you!"))
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/22-evm-signatures-selectors/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/22-evm-signatures-selectors/+page.md
index 0503ff802..ff7e1f3aa 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/22-evm-signatures-selectors/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/22-evm-signatures-selectors/+page.md
@@ -152,8 +152,7 @@ I mentioned there were a few different ways to acquire a function selector and t
We wont walk through all the different methods here, but I've provided some of them below and these are also available in the course's [**GitHub repo**](https://github.com/Cyfrin/foundry-nft-f23/blob/main/src/sublesson/CallAnything.sol).
-
-CallAnything.sol
+CallAnything.sol
```js
// SPDX-License-Identifier: MIT
@@ -248,13 +247,9 @@ contract CallAnything {
}
```
-
-
-
One last thing I want to point out is that we're not limited to this kind of interaction. Through this low-level calling method, two contracts are able to interact without possessing all the information associated with eachother. Consider this second contract `CallFunctionWithoutContract`.
-
-CallFunctionWithoutContract
+CallFunctionWithoutContract
```js
contract CallFunctionWithoutContract {
@@ -288,9 +283,6 @@ contract CallFunctionWithoutContract {
}
```
-
-
-
By passing this contract the address of our `CallAnything.sol` deployment. We're able to use the functions it possesses to interact with `CallAnything.sol`
::image{src='/foundry-nfts/22-evm-signatures-selectors/evm-signatures-selectors6.png' style='width: 100%; height: auto;'}
@@ -307,7 +299,7 @@ Now we can call `callTransferFunctionDirectlyThree` on our `CallFunctionWithoutC
Hopefully by now you can see the power available through this methodology of low-level calls. Now, despite hyping it up for several lessons, low-level calls are risky, and it's worth noting that they should be avoided when possible. Use an interface or something similar if you can, because low-level calls can leave you open to a number of potential issues and vulnerabilities.
-With that said, you've just learnt some really advanced stuff. If it's a little confusing, don't feel bad, you can always come back later when you've gained a little more experience and context of the EVM
+With that said, you've just learnt some really advanced stuff. If it's a little confusing, don't feel bad, you can always come back later when you've gained a little more experience and context of the EVM.
If you're excited to learn more about how Solidity works under-the-hood, I recommend reading through the [**Deconstructing Solidity**](https://blog.openzeppelin.com/deconstructing-a-solidity-contract-part-i-introduction-832efd2d7737) series by OpenZeppelin. It does a great job breaking things down in a very digestible and granular way.
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/3-foundry-setup/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/3-foundry-setup/+page.md
index 8cf337924..96b96640d 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/3-foundry-setup/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/3-foundry-setup/+page.md
@@ -8,18 +8,18 @@ _Follow along the course with this video._
### Foundry Setup
-Now that we know what an NFT is, let's investigate how we can build our own. As always the code we're writing will be available in the [**GitHub Repo**](https://github.com/Cyfrin/foundry-nft-f23) associate with this section.
+Now that we know what an NFT is, let's investigate how we can build our own. As always the code we're writing will be available in the [**GitHub Repo**](https://github.com/Cyfrin/foundry-nft-cu) associate with this section.
To start, let's initialize our workspace. Create a new directory in your course folder.
```bash
-mkdir foundry-nft-f23
+mkdir foundry-nft
```
We can then switch to this directory and open it in VSCode.
```bash
-cd foundry-nft-f23
+cd foundry-nft
code .
```
@@ -37,7 +37,7 @@ Now, as mentioned previously, NFTs are just another type of [**Token Standard**]
Begin by creating `src/BasicNft.sol` and setting up our usual boilerplate.
-```js
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
@@ -63,7 +63,7 @@ remappings = ["@openzeppelin/contracts=lib/openzeppelin-contracts/contracts"]
Now we can import and inherit the ERC721 contract into `BasicNft.sol`
-```js
+```solidity
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
@@ -75,7 +75,7 @@ contract BasicNft is ERC721 {}
Your IDE will likely indicate an error until we've passed the necessary arguments to the ERC721 constructor. You can ctrl + left-click (cmd + left-click) on the imported ERC721.sol to navigate to this contract and confirm what the constructor requires.
-```js
+```solidity
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
@@ -84,7 +84,7 @@ constructor(string memory name_, string memory symbol_) {
Just like the ERC20, we need to give our token a name and a symbol, that makes sense. Feel free to choose your own, but I'm going to go with the name `Doggie` and the symbol `DOG`.
-```js
+```solidity
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
@@ -99,7 +99,7 @@ contract BasicNft is ERC721 {
Great! While this contract may have the basic functionality of an NFT protocol, there's a lot to be done yet. Because each token is unique and possesses a unique tokenId, we absolutely need a token counter to track this in storage. We'll increment this each time a token is minted.
-```js
+```solidity
uint256 private s_tokenCounter;
constructor() ERC721("Doggie", "DOG"){
@@ -161,8 +161,7 @@ Both the tokenUri and imageUri for this example are hosted on IPFS (Inter-planet
So what's this tokenURI function going to look like for us? Well, our BasicNFT is going to also use IPFS, so similarly to our example above, we'll need to set up our function to return this string, pointing to the correct location in IPFS.
-```js
-
+```solidity
function tokenURI(uint256 tokenId) public view override returns (string memory) {}
```
diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/9-testnet-demo/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/9-testnet-demo/+page.md
index aadd39822..10f6705e3 100644
--- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/9-testnet-demo/+page.md
+++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/9-testnet-demo/+page.md
@@ -16,10 +16,9 @@ Before we get started, I'll mention you don't _have_ to do this yourself. This p
We'll be leveraging a Makefile for this again, I'll just be copying mine from the GitHub repo associated with this course, I've also provided it below for your conveninence.
-
-Makefile
+Makefile:
-```
+```make
-include .env
.PHONY: all test clean deploy fund help install snapshot format anvil
@@ -77,8 +76,6 @@ flipMoodNft:
@forge script script/Interactions.s.sol:FlipMoodNft $(NETWORK_ARGS)
```
-
-
Assuming our `.env` is ready to go, we should be able to run the following...
@@ -134,7 +131,7 @@ Rather than typing out long scripts, we'll use a makefile here. The associated G
In the makefile, we've captured most of the topics we've discussed so far, including our deploy script, which we'll use to deploy our basic NFT.
-::image{src='/foundry-nfts/9-testnet-demo/testnet1.png' style='width: 100%; height: auto;'}
+::image{src='/foundry-nfts/9-testnet-demo/testnet-demo1.png' style='width: 100%; height: auto;'}
Here is what the deploy script looks like:
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/1-defi-introduction/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/1-defi-introduction/+page.md
index 625992c8b..6893e58db 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/1-defi-introduction/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/1-defi-introduction/+page.md
@@ -71,7 +71,7 @@ Before we get too deep into DeFi, I do want to bring the concept of Miner/Maxima
At a very high-level, MEV is the process by which a node validator or miner orders the transactions of a block they're validating in such as way as to benefit themselves or conspirators.
-There are many teams and protocols working hard to mitigate the effects of MEV advantages, but for now, I recommend that anyone looking to get deep into DeFi read through and understand the content on [**flashbots.net's New to MEV guide**](https://docs.flashbots.net/new-to-mev). This content is both an entertaining way to learn about a complex concept and extremely eye-openning to the dangers MEV represents in the DeFi space.
+There are many teams and protocols working hard to mitigate the effects of MEV advantages, but for now, I recommend that anyone looking to get deep into DeFi read through and understand the content on [**flashbots.net's New to MEV guide**](https://docs.flashbots.net/new-to-mev). This content is both an entertaining way to learn about a complex concept and extremely eye-opening to the dangers MEV represents in the DeFi space.
### Wrap Up
@@ -79,11 +79,11 @@ The project we're going to be building in this section is a _stablecoin_. Stable
- AI gets better each day. Use assistants like [**ChatGPT**](https://chatgpt.com) for insight into your problems (beware hallucinations)
- [**The Discussions Tab**](https://github.com/Cyfrin/foundry-full-course-f23/discussions) of the course is a great place to start dialogues and seek assistance
-- [**Cyfrin's Discord Server**](https://discord.gg/cyfrin) contains thousands of likeminded students eager to help eachother succeed.
+- [**Cyfrin's Discord Server**](https://discord.gg/cyfrin) contains thousands of likeminded students eager to help each other succeed.
Like I said, there is _a lot_ to DeFi and this is easily going to be the most advanced section in this whole course. If you're able to succeed here you'll have reason to be exceptionally proud.
-The core is, DeFi is `permissionless`, `open-source`, finance and to _me_ is the best thing smart contracts enable. Through DeFi we can move away from contemporary financial instutions towards a fairer and more transparent finance system.
+The core is, DeFi is `permissionless`, `open-source`, finance and to _me_ is the best thing smart contracts enable. Through DeFi we can move away from contemporary financial institutions towards a fairer and more transparent finance system.
Let's get started walking through the project code.
@@ -127,8 +127,4 @@ Stepping into DeFi and understanding everything in this lesson can be a daunting
You can even check out Coinbase's educational content to get a headstart on DeFi.
-And remember,
-
-::image{src='/foundry-defi/1-defi-introduction/defi-introduction4.PNG' style='width: 100%; height: auto;'}
-
In the following section, we will be walking you through the code. Happy learning!
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/12-defi-deposit-and-mint/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/12-defi-deposit-and-mint/+page.md
index 66b9fe41d..287ffbebc 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/12-defi-deposit-and-mint/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/12-defi-deposit-and-mint/+page.md
@@ -262,7 +262,7 @@ Because this is one of our main functions, we're absolutely going to add some NA
* @param amountDscToMint: The amount of DecentralizedStableCoin to mint
* @notice: This function will deposit your collateral and mint DSC in one transaction
*/
-function depositCollateralAndMintDsc(address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDscToMint){
+function depositCollateralAndMintDsc(address tokenCollateralAddress, uint256 amountCollateral, uint256 amountDscToMint) external {
depositCollateral(tokenCollateralAddress, amountCollateral);
mintDsc(amountDscToMint);
}
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/13-defi-redeem-collateral/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/13-defi-redeem-collateral/+page.md
index 29eab29bc..c789e9231 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/13-defi-redeem-collateral/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/13-defi-redeem-collateral/+page.md
@@ -284,7 +284,7 @@ event CollateralDeposited(address indexed user, address indexed token, uint256 i
event CollateralRedeemed(address indexed user, address indexed token, uint256 indexed amount);
```
-At this point in our function, we'll want to transfer the redeemed tokens to the user, but we're caught in a trap of sorts. Part of our requirements for this function is that the user's `Health Factor` mustn't be broken after the transfer as occured. In situations like these, you may see the `CEI (Checks, Effects, Interactions)` pattern broken sometimes. A protocol _could_ call a function prior to the transfer to calculate changes and determine if the `Health Factor` is broken, before a transfer occurs, but this is often quite gas intensive. For this reason protocols will often sacrifice `CEI` for efficiency.
+At this point in our function, we'll want to transfer the redeemed tokens to the user, but we're caught in a trap of sorts. Part of our requirements for this function is that the user's `Health Factor` mustn't be broken after the transfer as occurred. In situations like these, you may see the `CEI (Checks, Effects, Interactions)` pattern broken sometimes. A protocol _could_ call a function prior to the transfer to calculate changes and determine if the `Health Factor` is broken, before a transfer occurs, but this is often quite gas intensive. For this reason protocols will often sacrifice `CEI` for efficiency.
```js
function redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral) public moreThanZero(amountCollateral) nonReentrant{
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/14-defi-liquidation-setup/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/14-defi-liquidation-setup/+page.md
index a0e0655e3..65fb6b161 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/14-defi-liquidation-setup/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/14-defi-liquidation-setup/+page.md
@@ -399,7 +399,7 @@ function liquidate(address collateral, address user, uint256 debtToCover) extern
uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);
- uint256 bonusCollateral = (tokeAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
+ uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
}
```
@@ -424,7 +424,7 @@ function liquidate(address collateral, address user, uint256 debtToCover) extern
uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);
- uint256 bonusCollateral = (tokeAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
+ uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
uint256 totalCollateralRedeemed = tokenAmountFromDebtCovered + bonusCollateral;
}
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/15-defi-liquidation-refactor/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/15-defi-liquidation-refactor/+page.md
index 6c0335baf..9a1473153 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/15-defi-liquidation-refactor/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/15-defi-liquidation-refactor/+page.md
@@ -207,7 +207,7 @@ contract DSCEngine is ReentrancyGuard {
}
uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);
- uint256 bonusCollateral = (tokeAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
+ uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
uint256 totalCollateralRedeemed = tokenAmountFromDebtCovered + bonusCollateral;
}
@@ -340,7 +340,7 @@ We'll add this new internal function under our `Private & Internal View Function
// Private & Internal View Functions //
///////////////////////////////////////////
-function _redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral, address from, address to){
+function _redeemCollateral(address tokenCollateralAddress, uint256 amountCollateral, address from, address to) private {
s_collateralDeposited[from][tokenCollateralAddress] -= amountCollateral;
emit CollateralRedeemed(msg.sender, tokenCollateralAddress, amountCollateral);
@@ -396,7 +396,7 @@ function liquidate(address collateral, address user, uint256 debtToCover) extern
}
uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);
- uint256 bonusCollateral = (tokeAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
+ uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
uint256 totalCollateralRedeemed = tokenAmountFromDebtCovered + bonusCollateral;
@@ -434,7 +434,7 @@ function liquidate(address collateral, address user, uint256 debtToCover) extern
}
uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);
- uint256 bonusCollateral = (tokeAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
+ uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
uint256 totalCollateralRedeemed = tokenAmountFromDebtCovered + bonusCollateral;
@@ -475,7 +475,7 @@ function liquidate(address collateral, address user, uint256 debtToCover) extern
}
uint256 tokenAmountFromDebtCovered = getTokenAmountFromUsd(collateral, debtToCover);
- uint256 bonusCollateral = (tokeAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
+ uint256 bonusCollateral = (tokenAmountFromDebtCovered * LIQUIDATION_BONUS) / LIQUIDATION_PRECISION;
uint256 totalCollateralRedeemed = tokenAmountFromDebtCovered + bonusCollateral;
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/16-defi-leveling-up-testing/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/16-defi-leveling-up-testing/+page.md
index aa227ac1d..5c4e4e8da 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/16-defi-leveling-up-testing/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/16-defi-leveling-up-testing/+page.md
@@ -137,7 +137,7 @@ contract SmallSol {
}
```
-In the above simple contract example, the obvious path is returning the `result of a + 1`. Another less obvious path would be this function `f` reverting due to overflow. Symbolic Execution, through it's mathetmatical modelling, would traverse all possible paths, looking for criteria that break our invariant. These paths might be represented something like this:
+In the above simple contract example, the obvious path is returning the `result of a + 1`. Another less obvious path would be this function `f` reverting due to overflow. Symbolic Execution, through it's mathematical modelling, would traverse all possible paths, looking for criteria that break our invariant. These paths might be represented something like this:
**Path 1:** `assert(a not 2**256 - 1); a:= a+1; return a;`
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/18-defi-handler-stateful-fuzz-tests/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/18-defi-handler-stateful-fuzz-tests/+page.md
index eefc3e512..3827ffe89 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/18-defi-handler-stateful-fuzz-tests/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/18-defi-handler-stateful-fuzz-tests/+page.md
@@ -100,7 +100,7 @@ import {DSCEngine} from "../../src/DSCEngine.sol";
import {DecentralizedStableCoin} from "../../src/DecentralizedStableCoin.sol";
import {HelperConfig} from "../../script/HelperConfig.s.sol";
-contract InvariantsTest is StdInvariant Test {
+contract InvariantsTest is StdInvariant, Test {
DeployDSC deployer;
DSCEngine dsce;
DecentralizedStableCoin dsc;
@@ -121,7 +121,7 @@ In order to test the invariant that our collateral value must always be more tha
...
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
...
-contract InvariantsTest is StdInvariant Test {
+contract InvariantsTest is StdInvariant, Test {
DeployDSC deployer;
DSCEngine dsce;
DecentralizedStableCoin dsc;
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/19-defi-handler-deposit-collateral/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/19-defi-handler-deposit-collateral/+page.md
index ae8d09766..0c930452b 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/19-defi-handler-deposit-collateral/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/19-defi-handler-deposit-collateral/+page.md
@@ -8,7 +8,7 @@ _Follow along the course with this video._
### Handler - Redeeming Collateral
-Ok! In this lesson we're going to adjust the code in our Invariants.t.sol such that our tests are more focused by being routed through a handler contract. In so doing, our tests will have a more sensible order of functions to call and more contexually relavent random data.
+Ok! In this lesson we're going to adjust the code in our Invariants.t.sol such that our tests are more focused by being routed through a handler contract. In so doing, our tests will have a more sensible order of functions to call and more contextually relevant random data.
We'll start by creating the Handler.t.sol contract.
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/23-defi-handler-price-feed/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/23-defi-handler-price-feed/+page.md
index e87c3ed90..a15e14e4b 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/23-defi-handler-price-feed/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/23-defi-handler-price-feed/+page.md
@@ -10,7 +10,7 @@ _Follow along the course with this video._
Our handler looks great at this point, but it doesn't reflect everything. Another powerful feature of this methodology is that we're able to leverage our handler to guide not only our target contract, but any contract we want!
-Take price feeds for example. These are external references that our protcol depends upon to function properly. We can use our handler to more realistically emulate how price feeds would behave in real-world scenarios.
+Take price feeds for example. These are external references that our protocol depends upon to function properly. We can use our handler to more realistically emulate how price feeds would behave in real-world scenarios.
Our project should already contain a MockV3Aggregator within the mocks folder, so let's begin by importing it into Handler.t.sol. This file mimics the behaviour of a price feed.
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/26-defi-recap/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/26-defi-recap/+page.md
index d25da33c8..eca14a22f 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/26-defi-recap/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/26-defi-recap/+page.md
@@ -29,7 +29,7 @@ We have 3 lessons remaining in this section, and they'll be a breeze compared to
See you soon!
-### Excercises
+### Exercises
[**Arbitrum NFT Challenge**](https://arbiscan.io/address/0x3DbBF2F9AcFB9Aac8E0b31563dd75a2D69148D64#code)
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/4-defi-decentralized-stablecoin/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/4-defi-decentralized-stablecoin/+page.md
index 431639c7d..9852fac7f 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/4-defi-decentralized-stablecoin/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/4-defi-decentralized-stablecoin/+page.md
@@ -18,8 +18,8 @@ All the code we discuss/write will of course be available on this section's [**G
Let's start by making our project directory.
```bash
-mkdir foundry-defi-stablecoin-f23
-cd foundry-defi-stablecoin-f23
+mkdir foundry-defi-stablecoin
+cd foundry-defi-stablecoin
code .
```
@@ -35,7 +35,7 @@ In our README.md, let's start taking some notes about our project and outlining
Our stablecoin is going to be:
-1. Relative Stability: Achored or Pegged to the US Dollar
+1. Relative Stability: Anchored or Pegged to the US Dollar
1. Chainlink Pricefeed
2. Function to convert ETH & BTC to USD
2. Stability Mechanism (Minting/Burning): Algorithmicly Decentralized
@@ -144,6 +144,13 @@ All of the properties of our protocol are going to be governed ultimately by the
In order to accomplish this, we're going to also inherit `Ownable` with DecentralizedStableCoin.sol. This will allow us to configure access control, assuring only our DSCEngine contract is authorized to call these functions.
+> ❗ **NOTE**
+> For version 5 of OpenZeppelin's Ownable contract, we need to pass an address
+> in the constructor. We have to modify our code to account for this when
+> running `forge build` so that our project will not error. Like this:
+> `constructor(address initialOwner) ERC20("DecentralizedStableCoin", "DSC")
+Ownerable(initialOwner) {}`
+
```js
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
@@ -166,8 +173,8 @@ function burn(uint256 _amount) external override onlyOwner{}
We're going to want to check for two things when this function is called.
-1. The amount burnt musn't be less than zero
-2. The amount burnt musn't be less than the user's balance
+1. The amount burnt must not be less than zero
+2. The amount burnt must not be more than the user's balance
We'll configure two custom errors for when these checks fail.
diff --git a/courses/advanced-foundry/3-develop-defi-protocol/6-defi-deposit-collateral/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/6-defi-deposit-collateral/+page.md
index 4c867dfcb..5faf7ec85 100644
--- a/courses/advanced-foundry/3-develop-defi-protocol/6-defi-deposit-collateral/+page.md
+++ b/courses/advanced-foundry/3-develop-defi-protocol/6-defi-deposit-collateral/+page.md
@@ -93,7 +93,7 @@ We'll probably want to initialize this mapping in our contract's constructor. To
constructor(address[] memory tokenAddresses, address[] memory priceFeedAddresses, address dscAddress){}
```
-Here's where we should definitely perform a sanity check, since a contract is only constructed once. If the indexes of our lists are meant to be mapped to eachother, we should assure the lengths of the lists match, and if they don't we can revert with another custom error.
+Here's where we should definitely perform a sanity check, since a contract is only constructed once. If the indexes of our lists are meant to be mapped to each other, we should assure the lengths of the lists match, and if they don't we can revert with another custom error.
```js
///////////////////
@@ -115,7 +115,7 @@ constructor(address[] memory tokenAddresses, address[] memory priceFeedAddresses
}
```
-Now we can add our for loop which will map our two lists of addresses to eachother.
+Now we can add our for loop which will map our two lists of addresses to each other.
```js
///////////////////
@@ -220,6 +220,11 @@ I've additionally included the nonReentrant modifier, which we'll need to import
Let's add the import to our contract.
+> ❗ **NOTE**
+> In version 5 of OpenZeppelin's contracts library, `ReentrancyGuard.sol` is
+> in a different location. Edit the filepath from `/security/` to `/utils/` will
+> work.
+
```js
pragma solidity ^0.8.18;
@@ -257,7 +262,7 @@ Now we can finally add the deposited collateral to our user's balance within our
* @param amountCollateral: The amount of collateral you're depositing
*/
function depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external moreThanZero(amountCollateral) isAllowedToken(tokenCollateralAddress) nonReentrant{
- s_collateralDeposited[msg.sender][tokenCollateralAddress] =+ amountCollateral;
+ s_collateralDeposited[msg.sender][tokenCollateralAddress] += amountCollateral;
}
```
@@ -281,7 +286,7 @@ event CollateralDeposited(address indexed user, address indexed token, uint256 i
* @param amountCollateral: The amount of collateral you're depositing
*/
function depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external moreThanZero(amountCollateral) isAllowedToken(tokenCollateralAddress) nonReentrant{
- s_collateralDeposited[msg.sender][tokenCollateralAddress] =+ amountCollateral;
+ s_collateralDeposited[msg.sender][tokenCollateralAddress] += amountCollateral;
emit CollateralDeposited(msg.sender, tokenCollateralAddress, amountCollateral);
}
```
@@ -308,7 +313,7 @@ import {IERC20} from "@openzeppelin/contracts/tokens/ERC20/IERC20.sol";
* @param amountCollateral: The amount of collateral you're depositing
*/
function depositCollateral(address tokenCollateralAddress, uint256 amountCollateral) external moreThanZero(amountCollateral) isAllowedToken(tokenCollateralAddress) nonReentrant{
- s_collateralDeposited[msg.sender][tokenCollateralAddress] =+ amountCollateral;
+ s_collateralDeposited[msg.sender][tokenCollateralAddress] += amountCollateral;
emit CollateralDeposited(msg.sender, tokenCollateralAddress, amountCollateral);
IERC20(tokenCollateralAddress).transferFrom(msg.sender, address(this), amountCollateral);
@@ -355,8 +360,7 @@ I've left our DSCEngine.sol (up to this point in the lesson) below for reference
See you in the next lesson!
-
-DSCEngine.sol
+DSCEngine.sol
```js
// Layout of Contract:
@@ -506,6 +510,3 @@ contract DSCEngine is ReentrancyGuard {
function getHealthFactor() external view {}
}
```
-
-
-
diff --git a/courses/advanced-foundry/4-merkle-airdrop/1-introduction/+page.md b/courses/advanced-foundry/4-merkle-airdrop/1-introduction/+page.md
index 494242684..6af895e14 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/1-introduction/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/1-introduction/+page.md
@@ -14,7 +14,7 @@ An airdrop occurs when a token development team distributes tokens or allows peo
::image{src='/foundry-merkle-airdrop/01-introduction/airdrop.png' style='width: 75%; height: auto;'}
-Tokens are tipically given for free, with eligibility criteria such as contributing to the project's GitHub repository or participating in the community. This process helps to _bootstrap the project_ by distributing tokens to a **list of eligible addresses**.
+Tokens are typically given for free, with eligibility criteria such as contributing to the project's GitHub repository or participating in the community. This process helps to _bootstrap the project_ by distributing tokens to a **list of eligible addresses**.
### Walkthrough
@@ -26,7 +26,7 @@ We will generate **scripts** to create Merkle Trees, Proofs, and Root Hash, as w
In this course, we will cover several topics besides Merkle Trees and Merkle Proofs, such as signatures, the ECDSA (Elliptical Curve Digital Signature) Algorithm, and transaction types.
-- After initializing a ZK Sync local node with Docker, we'll deploy the `Bagel` token and `MerkleAirdrop` contracts on it
+- After initializing a ZKsync local node with Docker, we'll deploy the `Bagel` token and `MerkleAirdrop` contracts on it
- We'll then **sign a message** to allow someone else to call `claim` on your behalf so you can receive the token while not paying for gas fees
- The initial supply of tokens is created and sent to the airdrop contract
- Finally, we can claim tokens on behalf of the claiming address (so they do not have to pay gas) using a signature
diff --git a/courses/advanced-foundry/4-merkle-airdrop/10-signature-standards/+page.md b/courses/advanced-foundry/4-merkle-airdrop/10-signature-standards/+page.md
index 426fadadd..ac5d70122 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/10-signature-standards/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/10-signature-standards/+page.md
@@ -8,7 +8,7 @@ _Follow along with the video_
### Introduction
-In this lesson, we will delve into Ethereum signature standards, specifically EIP 191 and EIP 712. We'll learn how to sign and verify signatures, and understand how these standards enhance data readability and security. Prior to these standards, signing transactions in Metamask resulted in unreadable messages, making it difficult to verify transaction data. EIP 191 and EIP 712 improve data readability and prevent replay attacks, which involve reusing a transaction or signature maliciously.
+In this lesson, we will delve into Ethereum signature standards, specifically EIP 191 and EIP 712. We'll learn how to sign and verify signatures, and understand how these standards enhance data readability and security. Prior to these standards, signing transactions in MetaMask resulted in unreadable messages, making it difficult to verify transaction data. EIP 191 and EIP 712 improve data readability and prevent replay attacks, which involve reusing a transaction or signature maliciously.
### Simple Signature Verification
diff --git a/courses/advanced-foundry/4-merkle-airdrop/12-transaction-types-introduction/+page.md b/courses/advanced-foundry/4-merkle-airdrop/12-transaction-types-introduction/+page.md
index bfc8c161d..435a2d403 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/12-transaction-types-introduction/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/12-transaction-types-introduction/+page.md
@@ -8,7 +8,7 @@ _Follow along with the video_
To gain a clearer understanding of transaction types, let's return to Remix and deploy again a simple smart contract, such as `SimpleStorage.sol`.
-We'll utilize the ZK Sync Remix plugin for deployment, ensuring our environment is set to 'wallet' and connected via MetaMask. After compiling the contract, we proceed to deploy it. But instead of sending a transaction directly, MetaMask will prompt us to **sign a message** 🖊️.
+We'll utilize the ZKsync Remix plugin for deployment, ensuring our environment is set to 'wallet' and connected via MetaMask. After compiling the contract, we proceed to deploy it. But instead of sending a transaction directly, MetaMask will prompt us to **sign a message** 🖊️.
This MetaMask **signature request** displays details about the message, including transaction type, sender, recipient, and gas limit. This message shows an EIP712 message, with a transaction type `113`. Upon signing, the Remix terminal will then show the signed message's details like type, recipient, sender, gasLimit, gasPerPubdataByteLimit.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/13-transaction-types/+page.md b/courses/advanced-foundry/4-merkle-airdrop/13-transaction-types/+page.md
index eee821fd9..fd69c3f4f 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/13-transaction-types/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/13-transaction-types/+page.md
@@ -8,7 +8,7 @@ _Follow along with the video_
### Introduction
-In this lesson, we will explore the **four** primary transaction types shared by both Ethereum and ZK Sync. After that, we'll take a look at the transaction types specific to the ZK Sync chain.
+In this lesson, we will explore the **four** primary transaction types shared by both Ethereum and ZKsync. After that, we'll take a look at the transaction types specific to the ZKsync chain.
### Type 0 (Legacy Transactions)
@@ -29,7 +29,7 @@ Introduced by EIP1559 during Ethereum's London fork, this transaction type aims
- Introduces the **Max Fee per Gas**, the total maximum fee the sender is willing to pay.
> 🗒️ **NOTE**:br
- > While ZK Sync supports type 2 transactions, it does not utilize the max fee parameters, as gas functions differently on ZK Sync.
+ > While ZKsync supports type 2 transactions, it does not utilize the max fee parameters, as gas functions differently on ZKsync.
### Type 3 (0x03)
@@ -44,14 +44,14 @@ Introduced by EIP4844, this transaction type provides an initial scaling solutio
The blob fee is deducted and burned from the sender's account before the transaction executes, meaning it is not refunded if the transaction fails.
-Next, we have two transaction types specific to ZK Sync:
+Next, we have two transaction types specific to ZKsync:
### Type 113 (0x71)
Defined by EIP712, these transactions standardize **data hashing** and **signing**, enabling features like **account abstraction** and **paymasters**.
> 👀❗**IMPORTANT**:br
-> Smart contracts on ZK Sync must be deployed using type 113 transactions.
+> Smart contracts on ZKsync must be deployed using type 113 transactions.
Fields specific to type 113 transactions are:
@@ -62,4 +62,4 @@ Fields specific to type 113 transactions are:
### Type 5 (0xFF) Transactions
-Known as **priority transactions**, these allow users to send transactions directly from L1 to L2 in ZK Sync.
+Known as **priority transactions**, these allow users to send transactions directly from L1 to L2 in ZKsync.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/14-blob-transactions/+page.md b/courses/advanced-foundry/4-merkle-airdrop/14-blob-transactions/+page.md
index 992a0e746..7d38919b7 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/14-blob-transactions/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/14-blob-transactions/+page.md
@@ -12,13 +12,13 @@ In a default transaction, data is stored and visible on-chain permanently. Blob
### EIP4844
-These transactions originate from EIP4844, also known as "Proto-DankSharding", introduced in the Dankun upgrade in March 2024. The purpose of this transaction type is to address Ethereum's high transaction costs.
+These transactions originate from EIP4844, also known as "Proto-DankSharding", introduced in the Dancun upgrade in March 2024. The purpose of this transaction type is to address Ethereum's high transaction costs.
Roll-ups help scale Ethereum by executing multiple transactions on their own chains, compressing them into batches, and submitting these batches back to Ethereum. Prior to the upgrade, all compressed transaction data had to be permanently stored on Ethereum nodes, which was inefficient and costly. With EIP4844, the data can be submitted **temporarily** for validation, avoiding permanent storage.
### Blobs and Transactions
-Blobs are temporary data attached to a transaction, which is then validated and deleted. For example, ZK Sync can send its batch of compressed transactions to Ethereum using blobs.
+Blobs are temporary data attached to a transaction, which is then validated and deleted. For example, ZKsync can send its batch of compressed transactions to Ethereum using blobs.
You can check [this transaction](https://etherscan.io/tx/0x291351476ef62e83ed33fb385f998232b8577bd1af60eb3463ce5a9e77fc8666) on Etherscan, which contains two [Blobs](https://etherscan.io/tx/0x291351476ef62e83ed33fb385f998232b8577bd1af60eb3463ce5a9e77fc8666#blobs). Clicking on one of them will reveal its massive data, which was not stored on-chain.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/15-understanding-type-113-transactions/+page.md b/courses/advanced-foundry/4-merkle-airdrop/15-understanding-type-113-transactions/+page.md
index fbc800630..32b874638 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/15-understanding-type-113-transactions/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/15-understanding-type-113-transactions/+page.md
@@ -6,7 +6,7 @@ _Follow along with the video_
---
-**Account abstraction** is a concept that allows users' assets to be stored in **smart contracts** rather than externally owned accounts (EOAs).This feature is buil directly in the ZK Sync, and it's said that this chain has _native_ account abstraction.
+**Account abstraction** is a concept that allows users' assets to be stored in **smart contracts** rather than externally owned accounts (EOAs).This feature is built directly in the ZKsync, and it's said that this chain has _native_ account abstraction.
On Ethereum, there are two types of accounts:
@@ -14,4 +14,4 @@ On Ethereum, there are two types of accounts:
2. **contract accounts**. These are smart contracts that behave as accounts but are also capable of implementing more complex logic. They enable features such as _multiple signers_ or allowing others to pay for gas and send transactions on our behalf.
> 🗒️ **NOTE**:br
-> Aaccounts on ZK Sync are automatically _smart contract_ accounts. They are fully customizable and include different signature schemes, multi-signature capabilities, and setting spending limits.
+> Accounts on ZKsync are automatically _smart contract_ accounts. They are fully customizable and include different signature schemes, multi-signature capabilities, and setting spending limits.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/18-test-on-zksync-(optional)/+page.md b/courses/advanced-foundry/4-merkle-airdrop/18-test-on-zksync-(optional)/+page.md
index 2439675b8..20a11312b 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/18-test-on-zksync-(optional)/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/18-test-on-zksync-(optional)/+page.md
@@ -9,18 +9,18 @@ _Follow along with the video_
> 🗒️ **NOTE**:br
> This lesson is optional
-We can also run our `MerkleAirdrop.t::testUsersCanClaim` test on the zkSync chain.
+We can also run our `MerkleAirdrop.t::testUsersCanClaim` test on the ZKsync chain.
-To do this, we can start by switching to the zkSync version by running `foundryup --zksync`. Since the zkSync compiler operates differently from the standard solc compiler, it's better to verify that everything builds correctly before deploying.
+To do this, we can start by switching to the ZKsync version by running `foundryup --zksync`. Since the ZKsync compiler operates differently from the standard solc compiler, it's better to verify that everything builds correctly before deploying.
```js
forge build --ZK Sync
```
> 🗒️ **NOTE**:br
-> If you encounter any warnings, they may be related to the use of `ecrecover`. These warnings can be safely ingnored since indicate that the accounts should use an ECDSA private key and should be EOAs. This warning are shown because the ZK Sync era supports native account abstraction.
+> If you encounter any warnings, they may be related to the use of `ecrecover`. These warnings can be safely ignored since indicate that the accounts should use an ECDSA private key and should be EOAs. This warning are shown because the ZKsync era supports native account abstraction.
-Finally, we can run our tests on zkSync with the following command:
+Finally, we can run our tests on ZKsync with the following command:
```js
forge test --zksync -vvv
diff --git a/courses/advanced-foundry/4-merkle-airdrop/2-project-setup/+page.md b/courses/advanced-foundry/4-merkle-airdrop/2-project-setup/+page.md
index e797fcca8..eaf390aa3 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/2-project-setup/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/2-project-setup/+page.md
@@ -14,7 +14,7 @@ We can begin by creating a repository for our project with the command `mkdir me
The token that we are going to airdrop will be a ERC20 token. In the same directory we can make a `BagelToken.sol` contract, where we will use the OpenZeppelin libraries `ERC20` and `Ownable` to create it. For that we first need to install the dependency with the command `forge install openzeppelin/openzeppelin-contracts --no-commit`.
-In the `foundry.toml` file we the spcify a remapping:
+In the `foundry.toml` file we the specify a remapping:
```toml
remappings = [ '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/']
@@ -66,7 +66,7 @@ function claim(address account) external {
}
```
-However, looping through an array that can grow indefinetely can lead to **performance issues** and calling this function. If there are for example, hundresds of claimers, will become cost prohibitive and will cause a Denial Of Service (DOS). Merkle trees will help solving this issue.
+However, looping through an array that can grow indefinately can lead to **performance issues** and calling this function. If there are for example, hundreds of claimers, will become cost prohibitive and will cause a Denial Of Service (DOS). Merkle trees will help solving this issue.
### Merkle Trees and Proofs
diff --git a/courses/advanced-foundry/4-merkle-airdrop/23-deploy-and-claim-on-zksync-local-node-(optional)/+page.md b/courses/advanced-foundry/4-merkle-airdrop/23-deploy-and-claim-on-zksync-local-node-(optional)/+page.md
index 98d416dd9..2171cdea4 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/23-deploy-and-claim-on-zksync-local-node-(optional)/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/23-deploy-and-claim-on-zksync-local-node-(optional)/+page.md
@@ -8,17 +8,17 @@ _Follow along with the video_
### Introduction
-In this lesson, we are going to deploy and claim our tokens on a **local zkSync node**. First, we need to terminate the Anvil node and then run `foundryup --zksync` to switch to the zkSync foundry environment.
+In this lesson, we are going to deploy and claim our tokens on a **local ZKsync node**. First, we need to terminate the Anvil node and then run `foundryup --zksync` to switch to the ZKsync foundry environment.
-⚠️ At the time of this recording, foundry scripts cannot be used on zkSync. Instead, we will use a bash script that runs the same commands as before. You can copy the [interactZk](https://github.com/Cyfrin/foundry-merkle-airdrop-cu/blob/main/interactZk.sh) script from the GitHub repository and follow along.
+⚠️ At the time of this recording, foundry scripts cannot be used on ZKsync. Instead, we will use a bash script that runs the same commands as before. You can copy the [interactZk](https://github.com/Cyfrin/foundry-merkle-airdrop-cu/blob/main/interactZk.sh) script from the GitHub repository and follow along.
### Script Overview
The script is organized into the following sections:
-1. It begins by declaring all the necessary variables. These include the default zkSync and Anvil local accounts and key pairs, along with the Merkle Root and the two Merkle Proofs.
+1. It begins by declaring all the necessary variables. These include the default ZKsync and Anvil local accounts and key pairs, along with the Merkle Root and the two Merkle Proofs.
-2. Using the zkSync CLI, a local zkSync node is started. The script then deploys the `BagelToken` and the `MerkleAirdrop` contracts.
+2. Using the ZKsync CLI, a local ZKsync node is started. The script then deploys the `BagelToken` and the `MerkleAirdrop` contracts.
3. It calls `MerkleAirdrop::getMessageHash` function using the default Anvil address and token amount to obtain the message hash. This message is then signed using `cast wallet sign` with the default Anvil key. The resulting signature is cleaned by removing the "0x" prefix and saved inside the file `signature.txt`.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/24-deploy-and-claim-on-zksync-sepolia-(optional)/+page.md b/courses/advanced-foundry/4-merkle-airdrop/24-deploy-and-claim-on-zksync-sepolia-(optional)/+page.md
index c6ab38a4e..9939f2bf4 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/24-deploy-and-claim-on-zksync-sepolia-(optional)/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/24-deploy-and-claim-on-zksync-sepolia-(optional)/+page.md
@@ -1,5 +1,5 @@
---
-title: Deploy and Claim on zkSync Sepolia
+title: Deploy and Claim on ZKsync Sepolia
---
_Follow along with the video_
@@ -8,9 +8,9 @@ _Follow along with the video_
### Introduction
-In this lesson, we will be **manually deploying on zkSync Sepolia**. Although scripts are highly recommended in order to avoid mistakes and save funds, we will proceed with typing command directly in the terminal since scripts do not work well on zkSync at the moment of recording.
+In this lesson, we will be **manually deploying on ZKsync Sepolia**. Although scripts are highly recommended in order to avoid mistakes and save funds, we will proceed with typing command directly in the terminal since scripts do not work well on ZKsync at the moment of recording.
-As usual, we will deploy the contracts `BagelToken` and `MerkleAirdrop`, generate the message hash, sign it, and split our long signature into its _v, r, s_ components. We'll then mint and trasfer tokens to the `MerkleAirdrop` contract, claim the tokens from a third party address and finally verify this claim.
+As usual, we will deploy the contracts `BagelToken` and `MerkleAirdrop`, generate the message hash, sign it, and split our long signature into its _v, r, s_ components. We'll then mint and transfer tokens to the `MerkleAirdrop` contract, claim the tokens from a third party address and finally verify this claim.
> 🗒️ **NOTE**:br
> In MetaMask, you can create a wallet, for example, "updraft," using keystores where accounts are pre-saved, preventing the need to use the private key directly. For this demonstration, _updraft_ will deploy the contracts while _updraft 2_ will handle token claims.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/25-summary/+page.md b/courses/advanced-foundry/4-merkle-airdrop/25-summary/+page.md
index c907083c4..0b710b4f6 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/25-summary/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/25-summary/+page.md
@@ -12,8 +12,8 @@ We began by exploring more efficient methods for data verification using **Merkl
Next, we delved into the use and mechanics of **signatures**, which are crucial for ensuring transaction authenticity and integrity. We learned how to generate signatures with `vm.sign` and `cast wallet sign`, and how to implement them within our smart contracts using OpenZeppelin's `ECDSA` library.
-Following this, we developed few scripts for deploying and interacting with our smart contracts on different platforms, including Anvil, zkSync local node, and zkSync Sepolia.
+Following this, we developed few scripts for deploying and interacting with our smart contracts on different platforms, including Anvil, ZKsync local node, and ZKsync Sepolia.
-Lastly, we examined various transaction types and the principles of the elliptic curve digital signature algorithm (eCDSA).
+Lastly, we examined various transaction types and the principles of the elliptic curve digital signature algorithm (ECDSA).
Well done! 🪩 🕺🏼
diff --git a/courses/advanced-foundry/4-merkle-airdrop/3-merkle-proofs/+page.md b/courses/advanced-foundry/4-merkle-airdrop/3-merkle-proofs/+page.md
index 86681296a..80c7eb7a6 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/3-merkle-proofs/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/3-merkle-proofs/+page.md
@@ -6,7 +6,7 @@ _Follow along with the video_
---
-### Introductioon
+### Introduction
Merkle Trees, Merkle Proofs, and Root Hashes are very important concepts in the realm of IT and blockchain technology. Invented by Ralph Merkle in 1979, a Merkle tree is a hierarchical structure where its base consists of **leaf nodes** representing data that has been hashed. The top of the tree is the **root hash**, created by hashing together pairs of adjacent nodes. This process continues up the tree, resulting in a single **root hash** that will represents all the data in the tree.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/6-merkle-tree-script/+page.md b/courses/advanced-foundry/4-merkle-airdrop/6-merkle-tree-script/+page.md
index 37e1915a8..21e72af67 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/6-merkle-tree-script/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/6-merkle-tree-script/+page.md
@@ -10,7 +10,7 @@ _Follow along with the video_
To access our private variables in the `MerkleAirdrop` contract, we need to add some getter functions like `getMerkleRoot` and `getAirdropToken`.
-We can then create a test file named `/test/MerkleAirdropTest.sol` and add some remappings in `foundy.toml`:
+We can then create a test file named `/test/MerkleAirdropTest.sol` and add some remappings in `foundry.toml`:
```toml
remappings = [
diff --git a/courses/advanced-foundry/4-merkle-airdrop/7-writing-the-tests/+page.md b/courses/advanced-foundry/4-merkle-airdrop/7-writing-the-tests/+page.md
index cccbae99f..7ac2bf64d 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/7-writing-the-tests/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/7-writing-the-tests/+page.md
@@ -38,16 +38,16 @@ token.mint(address(this), amountToSend);
token.transfer(address(airdrop), amountToSend);
```
-After this, we run the scripts again to generate the input and output files, updated with the user address. This involves executing `forge script script/GenerateInput.s.soul` to create the input file and `forge script script/MakeMerkle.s.soul` to generate the output file.
+After this, we run the scripts again to generate the input and output files, updated with the user address. This involves executing `forge script script/GenerateInput.s.sol` to create the input file and `forge script script/MakeMerkle.s.sol` to generate the output file.
### `MerkleAirdrop.t.sol`
In our test, we'll first store the user's initial balance, which is equal to zero. We then use the `vm.prank` cheat code to simulate the user calling the `claim` function on the `MerkleAirdrop` contract. The claim function requires the user address, the amount to claim, and the proof values as parameters, which can be copied from the output file we just generated. We then verify the ending balance of the user, ensuring it's equal to the claimed amount.
```js
- bytes32 proofOne = 0x0fd7c981d39bece61f7499702bf59b3114a90e66b51ba2c53abdf7b62986c00a;
- bytes32 proofTwo = 0xe5ebd1e1b5a5478a944ecab36a9a954ac3b6b8216875f6524caa7a1d87096576;
- bytes32[] proof = [proofOne, proofTwo];
+bytes32 proofOne = 0x0fd7c981d39bece61f7499702bf59b3114a90e66b51ba2c53abdf7b62986c00a;
+bytes32 proofTwo = 0xe5ebd1e1b5a5478a944ecab36a9a954ac3b6b8216875f6524caa7a1d87096576;
+bytes32[] proof = [proofOne, proofTwo];
//..
function testUsersCanClaim() public {
@@ -57,7 +57,7 @@ function testUsersCanClaim() public {
uint256 endingBalance = token.balanceOf(user);
console.log("Ending balance: %d", endingBalance);
assertEq(endingBalance - startingBalance, amountToCollect);
- }
+}
```
Finally, we run the test with the command
diff --git a/courses/advanced-foundry/4-merkle-airdrop/8-deployment-script/+page.md b/courses/advanced-foundry/4-merkle-airdrop/8-deployment-script/+page.md
index dad41c5b7..dcd98c15f 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/8-deployment-script/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/8-deployment-script/+page.md
@@ -44,7 +44,7 @@ To retrieve and use the last deployed contract in our `MerkleAirdrop.t.sol` file
forge install cyfrin/foundry-devops --no-commit
```
-Then, in the `setUp` function, add a check to determine if the current chain is zkSync:
+Then, in the `setUp` function, add a check to determine if the current chain is ZKsync:
```js
//..
@@ -64,4 +64,4 @@ function setUp() public {
}
```
-The `zkSyncChainChecker` determines if we are currently on a zkSync chain. If we are not, we deploy the contracts using our script and proceed with testing. Otherwise, we directly deploy new instances of the `BagelToken` and `MerkleAirdrop` contracts, mint the necessary tokens to the contract owner, and transfer the required amount of tokens to the `MerkleAirdrop` contract.
+The `zkSyncChainChecker` determines if we are currently on a ZKsync chain. If we are not, we deploy the contracts using our script and proceed with testing. Otherwise, we directly deploy new instances of the `BagelToken` and `MerkleAirdrop` contracts, mint the necessary tokens to the contract owner, and transfer the required amount of tokens to the `MerkleAirdrop` contract.
diff --git a/courses/advanced-foundry/4-merkle-airdrop/9-adding-signature-verification/+page.md b/courses/advanced-foundry/4-merkle-airdrop/9-adding-signature-verification/+page.md
index 468a1ccc6..69fbc817d 100644
--- a/courses/advanced-foundry/4-merkle-airdrop/9-adding-signature-verification/+page.md
+++ b/courses/advanced-foundry/4-merkle-airdrop/9-adding-signature-verification/+page.md
@@ -12,7 +12,7 @@ In this lesson, we will explore the process of allowing a third party to claim t
- A straightforward approach might involve **removing the account** from the equation entirely and relying solely on the caller of the function, the `msg.sender`. This would mean that each account would need to initiate the call themselves, covering their own gas fees. However, this method is limited and rigid, as restricts the ability of others to execute transactions on behalf of the account holder.
-- A more flexible solution involves allowing individuals to execute and pay for these transactions **on behalf of the account holder**, given that permission has been granted beforehead. This method can be achieved using digital signatures.
+- A more flexible solution involves allowing individuals to execute and pay for these transactions **on behalf of the account holder**, given that permission has been granted beforehand. This method can be achieved using digital signatures.
### Signatures
diff --git a/courses/blockchain-basics/1-basics/14-blockchain-fundamentals/+page.md b/courses/blockchain-basics/1-basics/14-blockchain-fundamentals/+page.md
index 4baac44f6..47c56a270 100644
--- a/courses/blockchain-basics/1-basics/14-blockchain-fundamentals/+page.md
+++ b/courses/blockchain-basics/1-basics/14-blockchain-fundamentals/+page.md
@@ -57,7 +57,7 @@ Proof of work is a system of sybil resistance used in many blockchains, in its e
Proof of Work needs to be combined with a `chain selection rule` to create `consensus`.
-A `chain selection rule` is implemented as a means to determine which blockchain is the _real_ blockchain. Bitcoin (and prior to the merge, Ethereum), both use something called `Nakomoto Consensus`. This is a combination of Proof of Work (Etherum has since switched to Proof of Stake) and the `longest chain rule`.
+A `chain selection rule` is implemented as a means to determine which blockchain is the _real_ blockchain. Bitcoin (and prior to the merge, Ethereum), both use something called `Nakamoto Consensus`. This is a combination of Proof of Work (Etherum has since switched to Proof of Stake) and the `longest chain rule`.
In the `longest chain rule`, the decentralized network decides that whichever chain has the most number of blocks will be the valid, or _real_ blockchain. When we saw `block confirmations` in Etherscan earlier, this was representing the number of blocks ahead of our transaction in the longest chain.
diff --git a/courses/foundry/1-foundry-simple-storage/25-compiling-foundry-zksync/+page.md b/courses/foundry/1-foundry-simple-storage/25-compiling-foundry-zksync/+page.md
index f0b153665..9d06c5c60 100644
--- a/courses/foundry/1-foundry-simple-storage/25-compiling-foundry-zksync/+page.md
+++ b/courses/foundry/1-foundry-simple-storage/25-compiling-foundry-zksync/+page.md
@@ -6,6 +6,6 @@ _Follow along with the video_
---
-> Previously, when we ran the `forge build` command, it generated an `/out` folder in the root project directory. This folder contains all the compilation details related the Ethereum Virtual Machine (EVM) and Vanilla Foundry. To compile for the ZKsync chain instead, we use the command `forge build --zk-sync`. This command creates a new folder in our project root called `/zk-out`, and contains all the compiled code compatible to the ZKsync Era VM.
+> Previously, when we ran the `forge build` command, it generated an `/out` folder in the root project directory. This folder contains all the compilation details related the Ethereum Virtual Machine (EVM) and Vanilla Foundry. To compile for the ZKsync chain instead, we use the command `forge build --zksync`. This command creates a new folder in our project root called `/zkout`, and contains all the compiled code compatible to the ZKsync Era VM.
If we need to revert to vanilla Foundry for deployment on the EVM, we simply run the command `foundryup` and then use `forge build`, which builds a standard Foundry project. Unless otherwise specified, we should continue using this method.
diff --git a/courses/foundry/2-foundry-fund-me/18-introduction-to-storage-optimization/+page.md b/courses/foundry/2-foundry-fund-me/18-introduction-to-storage-optimization/+page.md
index d314957de..d67a158a2 100644
--- a/courses/foundry/2-foundry-fund-me/18-introduction-to-storage-optimization/+page.md
+++ b/courses/foundry/2-foundry-fund-me/18-introduction-to-storage-optimization/+page.md
@@ -104,7 +104,7 @@ Please add the following function in your `FundMe.t.sol`:
function testPrintStorageData() public {
for (uint256 i = 0; i < 3; i++) {
bytes32 value = vm.load(address(fundMe), bytes32(i));
- console.log("Vaule at location", i, ":");
+ console.log("Value at location", i, ":");
console.logBytes32(value);
}
console.log("PriceFeed address:", address(fundMe.getPriceFeed()));
@@ -121,11 +121,11 @@ Run the test above by calling this in your terminal:
Ran 1 test for test/FundMe.t.sol:FundMeTest
[PASS] testPrintStorageData() (gas: 19138)
Logs:
- Vaule at location 0 :
+ Value at location 0 :
0x0000000000000000000000000000000000000000000000000000000000000000
- Vaule at location 1 :
+ Value at location 1 :
0x0000000000000000000000000000000000000000000000000000000000000000
- Vaule at location 2 :
+ Value at location 2 :
0x00000000000000000000000090193c961a926261b756d1e5bb255e67ff9498a1
PriceFeed address: 0x90193C961A926261B756D1E5bb255e67ff9498A1
diff --git a/courses/foundry/4-smart-contract-lottery/10-the-modulo-operation/+page.md b/courses/foundry/4-smart-contract-lottery/10-the-modulo-operation/+page.md
index b97421640..b51573c9b 100644
--- a/courses/foundry/4-smart-contract-lottery/10-the-modulo-operation/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/10-the-modulo-operation/+page.md
@@ -9,11 +9,11 @@ _Follow along with this video:_
We ended the previous lesson when we defined the following function:
-```javascript
+```solidity
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {}
```
-As we've said before, this function is going to be called by the VRF service. Here we will be given 1 random word (1 because of the NUM_WORDS we defined in the previous lesson). This isn't a `word` as in a string of letters like `pizza`, this is a big and random uint256. Being a number we can use it to do math.
+As we've said before, this function is going to be called by the VRF service. Here we will be given 1 random word (1 because of the `NUM_WORDS` we defined in the previous lesson). This isn't a `word` as in a string of letters like `pizza`, this is a big and random uint256. Being a number we can use it to do math.
What we need is to use the `modulo` operator denoted as `%` in Solidity.
@@ -47,40 +47,38 @@ This means that the player with index 1 (`s_players[1]`) is the winner of our ra
Enough theory, let's implement it in code!
-```javascript
- function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
- uint256 indexOfWinner = randomWords[0] % s_players.length;
- address payable winner = s_players[indexOfWinner];
- }
+```solidity
+function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
+ uint256 indexOfWinner = randomWords[0] % s_players.length;
+ address payable winner = s_players[indexOfWinner];
+}
```
Now let's record this last winner in state and send them their prize.
-```javascript
- function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
- uint256 indexOfWinner = randomWords[0] % s_players.length;
- address payable winner = s_players[indexOfWinner];
- s_recentWinner = winner;
- (bool success,) = winner.call{value:address(this).balance}("");
- if (!success) {
- revert Raffle__TransferFailed();
- }
+```solidity
+function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
+ uint256 indexOfWinner = randomWords[0] % s_players.length;
+ address payable winner = s_players[indexOfWinner];
+ s_recentWinner = winner;
+ (bool success,) = winner.call{value:address(this).balance}("");
+ if (!success) {
+ revert Raffle__TransferFailed();
}
+}
```
Let's define the `Raffle__TransferFailed()` custom error and the `s_recentWinner` variable in the state variables section.
-```javascript
- error Raffle__NotEnoughEthSent();
- error Raffle__TransferFailed();
+```solidity
+error Raffle__NotEnoughEthSent();
+error Raffle__TransferFailed();
- // Raffle related variables
- uint256 private immutable i_entranceFee;
- uint256 private immutable i_interval;
- uint256 private s_lastTimeStamp;
- address payable[] private s_players;
- address payable private s_recentWinner;
+// Raffle related variables
+uint256 private immutable i_entranceFee;
+uint256 private immutable i_interval;
+uint256 private s_lastTimeStamp;
+address payable[] private s_players;
+address payable private s_recentWinner;
```
Amazing! Let's keep going!
-
-
diff --git a/courses/foundry/4-smart-contract-lottery/11-implementing the lottery state-enum/+page.md b/courses/foundry/4-smart-contract-lottery/11-implementing the lottery state-enum/+page.md
index d55b8c778..304e9938d 100644
--- a/courses/foundry/4-smart-contract-lottery/11-implementing the lottery state-enum/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/11-implementing the lottery state-enum/+page.md
@@ -7,7 +7,7 @@ _Follow along with this video:_
### Introduction to the Concept of Enum
-In Solidity, `enum stands` for Enumerable. It is a user-defined data type that restricts a variable to have only one of the predefined values listed within the enum declaration. These predefined values are internally treated as unsigned integers, starting from 0 up to the count of elements minus one. Enums are useful for improving code readability and reducing potential errors by limiting the range of acceptable values for a variable. Read more about enums [here](https://docs.soliditylang.org/en/v0.8.26/types.html#enums).
+In Solidity, `enum` stands for Enumerable. It is a user-defined data type that restricts a variable to have only one of the predefined values listed within the enum declaration. These predefined values are internally treated as unsigned integers, starting from 0 up to the count of elements minus one. Enums are useful for improving code readability and reducing potential errors by limiting the range of acceptable values for a variable. Read more about enums [here](https://docs.soliditylang.org/en/v0.8.26/types.html#enums).
**How can we use enums in our Project?**
@@ -17,22 +17,22 @@ Let's code all these!
Paste the following code between the errors definition section and the state variables section:
-```javascript
- // Type declarations
- enum RaffleState {
- OPEN, // 0
- CALCULATING // 1
- }
+```solidity
+// Type declarations
+enum RaffleState {
+ OPEN, // 0
+ CALCULATING // 1
+}
- // Put this one in `Raffle related variables`
- RaffleState private s_raffleState;
+// Put this one in `Raffle related variables`
+RaffleState private s_raffleState;
```
Amazing, let's default our raffle state to open inside the constructor.
Add the following inside your constructor:
-```javascript
+```solidity
s_raffleState = RaffleState.OPEN;
```
@@ -42,42 +42,43 @@ Chainlink VRF has an [interesting page](https://docs.chain.link/vrf/v2-5/securit
Let's implement this in the code:
-```javascript
- function enterRaffle() external payable {
- if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
- if(s_raffleState != RaffleState.OPEN) revert Raffle__RaffleNotOpen(); // If not open you don't enter.
-
- s_players.push(payable(msg.sender));
- emit EnteredRaffle(msg.sender);
- }
+```solidity
+function enterRaffle() external payable {
+ if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
+ if(s_raffleState != RaffleState.OPEN) revert Raffle__RaffleNotOpen(); // If not open you don't enter.
+
+ s_players.push(payable(msg.sender));
+ emit EnteredRaffle(msg.sender);
+}
```
Make sure to also define the new `Raffle__RaffleNotOpen()` error.
Great, now let's also change the state of the Raffle when we commence the process of picking the winner.
-```javascript
- function pickWinner() external {
- // check to see if enough time has passed
- if (block.timestamp - s_lastTimeStamp < i_interval) revert();
+```solidity
+function pickWinner() external {
+ // check to see if enough time has passed
+ if (block.timestamp - s_lastTimeStamp < i_interval) revert();
- s_raffleState = RaffleState.CALCULATING;
+ s_raffleState = RaffleState.CALCULATING;
+}
```
The last thing we need to do is to reopen the Raffle after we pick the winner inside `fulfillRandomWords` function.
-```javascript
- function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
- uint256 indexOfWinner = randomWords[0] % s_players.length;
- address payable winner = s_players[indexOfWinner];
- s_recentWinner = winner;
- s_raffleState = RaffleState.OPEN;
- (bool success,) = winner.call{value:address(this).balance}("");
- if (!success) {
- revert Raffle__TransferFailed();
- }
+```solidity
+function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
+ uint256 indexOfWinner = randomWords[0] % s_players.length;
+ address payable winner = s_players[indexOfWinner];
+ s_recentWinner = winner;
+ s_raffleState = RaffleState.OPEN;
+ (bool success,) = winner.call{value:address(this).balance}("");
+ if (!success) {
+ revert Raffle__TransferFailed();
}
+}
```
I know you thought about it: `But why are we opening the Raffle again? We've selected a winner but the s_players array is still full!` And you are right!
-We will take care of this in the next lesson!
\ No newline at end of file
+We will take care of this in the next lesson!
diff --git a/courses/foundry/4-smart-contract-lottery/12-lottery-restart-resetting-an-array/+page.md b/courses/foundry/4-smart-contract-lottery/12-lottery-restart-resetting-an-array/+page.md
index be6126312..be77076f4 100644
--- a/courses/foundry/4-smart-contract-lottery/12-lottery-restart-resetting-an-array/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/12-lottery-restart-resetting-an-array/+page.md
@@ -11,15 +11,15 @@ Continuing from where we left in the last lesson. We've picked the winner, we've
We add the following line inside the `fulfillRandomWords` function:
-```javascript
+```solidity
s_players = new address payable[](0);
```
-This initializes a new empty array over the existing array, which is another way of saying `we wipe out the existing array`.
+This initializes a new empty array over the existing array, which is another way of saying **we wipe out the existing array**.
Additionally, given that we are starting up a fresh raffle, we also need to bring the `s_lastTimeStamp` to the present time.
-```javascript
+```solidity
s_lastTimeStamp = block.timestamp;
```
@@ -31,4 +31,4 @@ And emit it as the last line of the `fulfillRandomWords` function: `emit PickedW
Run a `forge build` to make sure everything compiles.
-Great job!
\ No newline at end of file
+Great job!
diff --git a/courses/foundry/4-smart-contract-lottery/14-the-cei-method-checks-efects-interactions/+page.md b/courses/foundry/4-smart-contract-lottery/14-the-cei-method-checks-efects-interactions/+page.md
index 9e257946a..30c74be17 100644
--- a/courses/foundry/4-smart-contract-lottery/14-the-cei-method-checks-efects-interactions/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/14-the-cei-method-checks-efects-interactions/+page.md
@@ -9,7 +9,7 @@ _Follow along with this video:_
A very important thing to note. When developing this contract Patrick is using a style called Checks-Effects-Interactions or CEI.
-The Checks-Effects-Interactions pattern is a crucial best practice in Solidity development aimed at enhancing the security of smart contracts, especially against re-entrancy attacks. This pattern structures the code within a function into three distinct phases:
+The Checks-Effects-Interactions pattern is a crucial best practice in Solidity development aimed at enhancing the security of smart contracts, especially against reentrancy attacks. This pattern structures the code within a function into three distinct phases:
- Checks: Validate inputs and conditions to ensure the function can execute safely. This includes checking permissions, input validity, and contract state prerequisites.
- Effects: Modify the state of our contract based on the validated inputs. This phase ensures that all internal state changes occur before any external interactions.
@@ -17,31 +17,31 @@ The Checks-Effects-Interactions pattern is a crucial best practice in Solidity d
Another important reason for using CEI in your smart contract is gas efficiency. Let's go through a small example:
-```javascript
- function coolFunction() public {
- sendA();
- callB();
- checkX();
- checkY();
- updateM();
- }
+```solidity
+function coolFunction() public {
+ sendA();
+ callB();
+ checkX();
+ checkY();
+ updateM();
+}
```
In the function above what happens if `checkX()` fails? The EVM goes through a function from top to bottom. That means it will execute `sendA()` then `callB()` then attempt `checkX()` which will fail, and then all the things need to be reverted. Every single operation costs gas, we pay for everything, and we just performed 2 operations, to revert at the 3rd. From this perspective isn't the following more logical?
-```javascript
- function coolFunction() public {
- // Checks
- checkX();
- checkY();
-
- // Effects
- updateStateM();
-
- // Interactions
- sendA();
- callB();
- }
+```solidity
+function coolFunction() public {
+ // Checks
+ checkX();
+ checkY();
+
+ // Effects
+ updateStateM();
+
+ // Interactions
+ sendA();
+ callB();
+}
```
First, we do the checks, if something goes bad we revert, but we don't spend that much gas. Then, if checks pass, we do effects, and all internal state changes are performed, these usually can't fail, or if they fail they spend an amount of gas that we can control. Lastly, we perform the interactions, here we send the tokens or ETH or perform external calls to other contracts. We wouldn't want these to happen in the absence of the checks or the state update so it's more logical to put them last.
diff --git a/courses/foundry/4-smart-contract-lottery/15-introduction-to-chainlink-automation/+page.md b/courses/foundry/4-smart-contract-lottery/15-introduction-to-chainlink-automation/+page.md
index ca820ae00..efd37a452 100644
--- a/courses/foundry/4-smart-contract-lottery/15-introduction-to-chainlink-automation/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/15-introduction-to-chainlink-automation/+page.md
@@ -9,7 +9,7 @@ _Follow along with this video:_
Amazing work! Our project starts looking good!
-Looking through it we can see that there's an obvious problem. For the winner to be picked we need someone to call `pickWinner`. Manually calling this day after day is not optimal, and we, as engineers, need to come up with a better solution! Let's discuss Chainlink Automation!
+Looking through it we can see that there's an obvious problem. For the winner to be picked we need someone to call `pickWinner`. Manually calling this day after day is not optimal, and we, as engineers, need to come up with a better solution! Let's discuss Chainlink Automation!
**Chainlink Automation** is a decentralized service designed to automate key functions and DevOps tasks within smart contracts in a highly reliable, trust-minimized, and cost-efficient manner. It allows smart contracts to automatically execute transactions based on predefined conditions or schedules.
@@ -21,7 +21,7 @@ Let's open the contract available [here](https://docs.chain.link/chainlink-autom
Following Richard's tutorial let's delete the `is AutomationCompatibleInterface` inheritance, both the `interval` and `lastTimeStamp` variables, adjust the constructor and delete both available functions. Create a new function called `count` which increments the `counter` state variable. It should look like this:
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
@@ -102,4 +102,4 @@ From time to time go back to Remix and check the `counter` value. You'll see it
Ok, this was fun, let's pause/cancel the upkeep to save some of that sweet testnet LINK.
-Amazing work!
\ No newline at end of file
+Amazing work!
diff --git a/courses/foundry/4-smart-contract-lottery/16-implementing-chainlink-automation/+page.md b/courses/foundry/4-smart-contract-lottery/16-implementing-chainlink-automation/+page.md
index 688f37ce5..ec8cf6939 100644
--- a/courses/foundry/4-smart-contract-lottery/16-implementing-chainlink-automation/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/16-implementing-chainlink-automation/+page.md
@@ -11,25 +11,25 @@ Remember how Richard deleted the `performUpkeep` and `checkUpkeep` in the previo
For this to work we need to refactor the `pickWinner` function. That functionality needs to be part of the `performUpkeep` if we want the Chainlink node to call it for us. But before that, let's create the `checkUpkeep` function:
-```javascript
- /**
- * @dev This is the function that the Chainlink Keeper nodes call
- * they look for `upkeepNeeded` to return True.
- * the following should be true for this to return true:
- * 1. The time interval has passed between raffle runs.
- * 2. The lottery is open.
- * 3. The contract has ETH.
- * 4. There are players registered.
- * 5. Implicity, your subscription is funded with LINK.
- */
- function checkUpkeep(bytes memory /* checkData */) public view returns (bool upkeepNeeded, bytes memory /* performData */) {
- bool isOpen = RaffleState.OPEN == s_raffleState;
- bool timePassed = ((block.timestamp - s_lastTimeStamp) >= i_interval);
- bool hasPlayers = s_players.length > 0;
- bool hasBalance = address(this).balance > 0;
- upkeepNeeded = (timePassed && isOpen && hasBalance && hasPlayers);
- return (upkeepNeeded, "0x0");
- }
+```solidity
+/**
+ * @dev This is the function that the Chainlink Keeper nodes call
+ * they look for `upkeepNeeded` to return True.
+ * the following should be true for this to return true:
+ * 1. The time interval has passed between raffle runs.
+ * 2. The lottery is open.
+ * 3. The contract has ETH.
+ * 4. There are players registered.
+ * 5. Implicity, your subscription is funded with LINK.
+ */
+function checkUpkeep(bytes memory /* checkData */) public view returns (bool upkeepNeeded, bytes memory /* performData */) {
+ bool isOpen = RaffleState.OPEN == s_raffleState;
+ bool timePassed = ((block.timestamp - s_lastTimeStamp) >= i_interval);
+ bool hasPlayers = s_players.length > 0;
+ bool hasBalance = address(this).balance > 0;
+ upkeepNeeded = (timePassed && isOpen && hasBalance && hasPlayers);
+ return (upkeepNeeded, "0x0");
+}
```
Again, a lot of things up there, but fear not, we are going to explain everything.
@@ -40,26 +40,26 @@ You are amazing! Keep going!
Back to our raffle now, what are the conditions required to be true in order to commence the winner-picking process? We've placed the answer to this in the NATSPEC comments.
-```javascript
- * 1. The time interval has passed between raffle runs.
- * 2. The lottery is open.
- * 3. The contract has ETH.
- * 4. There are players registered.
- * 5. Implicity, your subscription is funded with LINK.
+```solidity
+ * 1. The time interval has passed between raffle runs.
+ * 2. The lottery is open.
+ * 3. The contract has ETH.
+ * 4. There are players registered.
+ * 5. Implicity, your subscription is funded with LINK.
```
For points 1-3 we coded the following lines:
-```javascript
- bool isOpen = RaffleState.OPEN == s_raffleState;
- bool timePassed = ((block.timestamp - s_lastTimeStamp) > i_interval);
- bool hasPlayers = s_players.length > 0;
- bool hasBalance = address(this).balance > 0;
+```solidity
+bool isOpen = RaffleState.OPEN == s_raffleState;
+bool timePassed = ((block.timestamp - s_lastTimeStamp) > i_interval);
+bool hasPlayers = s_players.length > 0;
+bool hasBalance = address(this).balance > 0;
```
We check if the Raffle is in the open state, if enough time has passed and if there are players registered to the Raffle and if we have a prize to give out. All these need to be true for the winner-picking process to be able to run.
In the end, we return the two elements required by the function declaration:
-```javascript
+```solidity
return (upkeepNeeded, "0x0");
```
@@ -69,29 +69,29 @@ Amazing!
Chainlink nodes will call this `checkUpkeep` function. If the return `upkeepNeeded` is true, then they will call `performUpkeep` ... which in our case is the `pickWinner` function. Let's refactor it a little bit:
-```javascript
- // 1. Get a random number
- // 2. Use the random number to pick a player
- // 3. Automatically called
- function performUpkeep(bytes calldata /* performData */) external override {
- (bool upkeepNeeded, ) = checkUpkeep("");
- // require(upkeepNeeded, "Upkeep not needed");
- if (!upkeepNeeded) {
- revert Raffle__UpkeepNotNeeded(
- address(this).balance,
- s_players.length,
- uint256(s_raffleState)
- );
- }
- s_raffleState = RaffleState.CALCULATING;
- uint256 requestId = i_vrfCoordinator.requestRandomWords(
- i_gasLane,
- i_subscriptionId,
- REQUEST_CONFIRMATIONS,
- i_callbackGasLimit,
- NUM_WORDS
+```solidity
+// 1. Get a random number
+// 2. Use the random number to pick a player
+// 3. Automatically called
+function performUpkeep(bytes calldata /* performData */) external override {
+ (bool upkeepNeeded, ) = checkUpkeep("");
+ // require(upkeepNeeded, "Upkeep not needed");
+ if (!upkeepNeeded) {
+ revert Raffle__UpkeepNotNeeded(
+ address(this).balance,
+ s_players.length,
+ uint256(s_raffleState)
);
}
+ s_raffleState = RaffleState.CALCULATING;
+ uint256 requestId = i_vrfCoordinator.requestRandomWords(
+ i_gasLane,
+ i_subscriptionId,
+ REQUEST_CONFIRMATIONS,
+ i_callbackGasLimit,
+ NUM_WORDS
+ );
+}
```
We copied the start from the [Chainlink Automation tutorial](https://docs.chain.link/chainlink-automation/guides/compatible-contracts) renaming the `pickWinner` function. Given that our new `performUpkeep` is external, as it should be if we want one of the Chainlink nodes to call it, we need to ensure that the same conditions are required for everyone else to call it. In other words, there are two possibilities for this function to be called:
@@ -107,24 +107,24 @@ For that we will make the function perform a call to `checkUpkeep`:
And we check it's result. If the result is false we revert with a new custom error:
-```javascript
- if (!upkeepNeeded) {
- revert Raffle__UpkeepNotNeeded(
- address(this).balance,
- s_players.length,
- uint256(s_raffleState)
- );
- }
+```solidity
+if (!upkeepNeeded) {
+ revert Raffle__UpkeepNotNeeded(
+ address(this).balance,
+ s_players.length,
+ uint256(s_raffleState)
+ );
+}
```
Let's define it at the top of the contract, next to the other errors:
-```javascript
- error Raffle__UpkeepNotNeeded(
- uint256 currentBalance,
- uint256 numPlayers,
- uint256 raffleState
- );
+```solidity
+error Raffle__UpkeepNotNeeded(
+ uint256 currentBalance,
+ uint256 numPlayers,
+ uint256 raffleState
+);
```
This is the first time when we provided some parameters to the error. Think about them as extra info you get when you receive the error.
@@ -135,17 +135,16 @@ We leave the rest of the function intact.
Another thing that we should do is to import the `AutomationCompatibleInterface`:
-```javascript
+```solidity
import {AutomationCompatibleInterface} from "chainlink/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol";
```
and let's make our contract inherit it:
-```javascript
+```solidity
contract Raffle is VRFConsumerBaseV2, AutomationCompatibleInterface {
```
Now let's call a `forge build` to see if everything is ok.
Amazing work!
-
diff --git a/courses/foundry/4-smart-contract-lottery/18-mid-section-recap/+page.md b/courses/foundry/4-smart-contract-lottery/18-mid-section-recap/+page.md
index da64ccc33..bed8c03f3 100644
--- a/courses/foundry/4-smart-contract-lottery/18-mid-section-recap/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/18-mid-section-recap/+page.md
@@ -11,12 +11,12 @@ Congratulations, we wrote a bunch of great code!
What did we do?
-- We implemented Chainlink VRF to get a random number;
-- We defined a couple of variables that we need both for Raffle operation and for Chainlink VRF interaction;
-- We have a not-so-small constructor;
-- We've created a method for the willing participants to enter the Raffle;
+- We implemented Chainlink VRF to get a random number
+- We defined a couple of variables that we need both for Raffle operation and for Chainlink VRF interaction
+- We have a not-so-small constructor
+- We created a method for the willing participants to enter the Raffle
- Then made the necessary integrations with Chainlink Automation to automatically draw a winner when the time is right.
-- When the time is right and after the Chainlink nodes perform the call then Chainlink VRF will provide the requested randomness inside `fulfillRandomWords`;
+- When the time is right and after the Chainlink nodes perform the call then Chainlink VRF will provide the requested randomness inside `fulfillRandomWords`
- The randomness is used to find out who won, the prize is sent, raffle is reset.
-And that's all! Take a break, take a walk and come back for more fun activities. Next, we'll have deploying, testing and many refactorings!
\ No newline at end of file
+And that's all! Take a break, take a walk and come back for more fun activities. Next, we'll have deploying, testing and many refactorings!
diff --git a/courses/foundry/4-smart-contract-lottery/19-tests-and-deploy-the-lotterys-smart-contract-pt1/+page.md b/courses/foundry/4-smart-contract-lottery/19-tests-and-deploy-the-lotterys-smart-contract-pt1/+page.md
index bf27e1782..97c63bcc1 100644
--- a/courses/foundry/4-smart-contract-lottery/19-tests-and-deploy-the-lotterys-smart-contract-pt1/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/19-tests-and-deploy-the-lotterys-smart-contract-pt1/+page.md
@@ -17,35 +17,37 @@ First, import the newly created HelperConfig.
Then, modify the run function:
-```javascript
- function run() external returns (Raffle, HelperConfig) {
- HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!
- (
- uint256 entranceFee;
- uint256 interval;
- address vrfCoordinator;
- bytes32 gasLane;
- uint64 subscriptionId;
- uint32 callbackGasLimit;
+```solidity
+function run() external returns (Raffle, HelperConfig) {
+ HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!
+ (
+ uint256 entranceFee;
+ uint256 interval;
+ address vrfCoordinator;
+ bytes32 gasLane;
+ uint64 subscriptionId;
+ uint32 callbackGasLimit;
+
+ ) = helperConfig.activeNetworkConfig();
- ) = helperConfig.activeNetworkConfig();
+}
```
Great! Now that we have deconstructed the NetworkConfig we have all the variables we need to deploy::
-```javascript
- vm.startBroadcast();
- Raffle raffle = new Raffle(
- entranceFee,
- interval,
- vrfCoordinator,
- gasLane,
- subscriptionId,
- callbackGasLimit
- )
- vm.stopBroadcast();
-
- return raffle;
+```solidity
+vm.startBroadcast();
+Raffle raffle = new Raffle(
+ entranceFee,
+ interval,
+ vrfCoordinator,
+ gasLane,
+ subscriptionId,
+ callbackGasLimit
+)
+vm.stopBroadcast();
+
+return raffle;
```
We use the `vm.startBroadcast` and `vm.stopBroadcast` commands to indicate that we are going to send a transaction. The transaction is the deployment of a new `Raffle` contract using the parameters we've obtained from the `HelperConfig`. In the end, we are returning the newly deployed contract.
@@ -60,7 +62,7 @@ Let's start writing the first test. You've already done this at least two times
Your unit test should start like this:
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
@@ -79,37 +81,37 @@ We've declared the SPDX-License-Identifier, the solidity version, imported the `
In `DeployRaffle.s.sol` we need to make sure that `run` also returns the `HelperConfig` contract:
-```javascript
- function run() external returns (Raffle, HelperConfig) {
- HelperConfig helperConfig = new HelperConfig();
- (
+```solidity
+function run() external returns (Raffle, HelperConfig) {
+ HelperConfig helperConfig = new HelperConfig();
+ (
uint256 entranceFee,
uint256 interval,
address vrfCoordinator,
bytes32 gasLane,
uint64 subscriptionId,
uint32 callbackGasLimit
- ) = helperConfig.activeNetworkConfig();
+ ) = helperConfig.activeNetworkConfig();
- vm.startBroadcast();
- Raffle raffle = new Raffle(
- entranceFee,
- interval,
- vrfCoordinator,
- gasLane,
- subscriptionId,
- callbackGasLimit
- );
- vm.stopBroadcast();
+ vm.startBroadcast();
+ Raffle raffle = new Raffle(
+ entranceFee,
+ interval,
+ vrfCoordinator,
+ gasLane,
+ subscriptionId,
+ callbackGasLimit
+ );
+ vm.stopBroadcast();
- return (raffle, helperConfig);
- }
+ return (raffle, helperConfig);
+}
```
Next comes the state variables and `setUp` function in `RaffleTest.t.sol`:
-```javascript
+```solidity
contract RaffleTest is Test {
Raffle public raffle;
@@ -159,18 +161,18 @@ Amazing! With all these done let's write a small test to ensure our `setUp` is f
First, we need a getter function to retrieve the raffle state. Put the following towards the end of the `Raffle.sol`:
-```javascript
- function getRaffleState() public view returns (RaffleState) {
- return s_raffleState;
- }
+```solidity
+function getRaffleState() public view returns (RaffleState) {
+ return s_raffleState;
+}
```
Inside `RaffleTest.t.sol` paste the following test:
-```javascript
- function testRaffleInitializesInOpenState() public view {
- assert(raffle.getRaffleState() == Raffle.RaffleState.OPEN);
- }
+```solidity
+function testRaffleInitializesInOpenState() public view {
+ assert(raffle.getRaffleState() == Raffle.RaffleState.OPEN);
+}
```
**Note**: we used `Raffle.RaffleState.OPEN` to get the value attributed to `OPEN` inside the `RaffleState` enum. This is possible because `RaffleState` is considered a [type](https://docs.soliditylang.org/en/latest/types.html#enums). So we can access that by calling the type `RaffleState` inside a `Raffle` contract to retrieve the `OPEN` value.
@@ -190,4 +192,4 @@ Ran 1 test suite in 2.25s (12.42ms CPU time): 1 tests passed, 0 failed, 0 skippe
Ok, so our Raffle starts in an OPEN state. Exactly like we coded it!
-Great job! We started testing, let's see what we can do next!
\ No newline at end of file
+Great job! We started testing, let's see what we can do next!
diff --git a/courses/foundry/4-smart-contract-lottery/2-smart-contract-lottery-project-setup/+page.md b/courses/foundry/4-smart-contract-lottery/2-smart-contract-lottery-project-setup/+page.md
index 1febb61d7..7b75c87b3 100644
--- a/courses/foundry/4-smart-contract-lottery/2-smart-contract-lottery-project-setup/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/2-smart-contract-lottery-project-setup/+page.md
@@ -11,8 +11,8 @@ Prepare yourself because this next project will be extremely awesome!
For the project, we'll be working with an advanced lottery or raffle smart contract. This won't just be an exercise in coding, but a chance to learn more about:
-- Events;
-- On-chain randomness (done the proper way);
+- Events
+- On-chain randomness (done the proper way)
- Chainlink automation
- And many more!
@@ -64,28 +64,28 @@ We will introduce the Chainlink integrations in future lessons. For now, remembe
Inside the `src` folder create a file named `Raffle.sol`. Inside the newly created file, we start our new project as always:
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
-contract Raffe{
-
+contract Raffle {
+
}
```
As you might know already having a strong NATSPEC is a key element in developing a nicely structured and readable smart contract. Let's create a NATSPEC description above the contract declaration line:
-```javascript
+```solidity
/**
* @title A sample Raffle Contract
* @author Patrick Collins (or even better, you own name)
* @notice This contract is for creating a sample raffle
- * @dev It implements Chainlink VRFv2 and Chainlink Automation
+ * @dev It implements Chainlink VRFv2.5 and Chainlink Automation
*/
-contract Raffle{
-
+contract Raffle {
+
}
```
@@ -94,7 +94,7 @@ Let's think about the structure of our project, what is the main functionality a
1. Users should be able to enter the raffle by paying a ticket price;
2. At some point, we should be able to pick a winner out of the registered users.
-```javascript
+```solidity
contract Raffle{
function enterRaffle() public {}
@@ -104,8 +104,8 @@ contract Raffle{
Good! Given that users need to pay for a ticket, we need to define the price of this ticket and also make the `enterRaffle` function `payable` to be able to receive the user's ETH. Every time we introduce a new state variable we need to think about what type of variable we need to use. Should we make the `entranceFee` constant, immutable or simply private? Why private and not public? The best solution is to make it a private immutable, so we get to define it once at the constructor level. If we decide to create a new raffle we simply redeploy the contract and change the `entranceFee`. Ok, but we need people to be able to see what they should pay as `entranceFee`. To facilitate this we will create a getter function.
-```javascript
-contract Raffe{
+```solidity
+contract Raffle {
uint256 private immutable i_entranceFee;
constructor(uint256 entranceFee) {
diff --git a/courses/foundry/4-smart-contract-lottery/20-deploy-script/+page.md b/courses/foundry/4-smart-contract-lottery/20-deploy-script/+page.md
index 8f4759bef..ed759d7ce 100644
--- a/courses/foundry/4-smart-contract-lottery/20-deploy-script/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/20-deploy-script/+page.md
@@ -8,7 +8,7 @@ _Follow along with the video_
Let's begin by creating a new file in the `/script` directory called `DeployRaffle.sol` and importing the `Raffle` contract.
-```js
+```solidity
pragma solidity ^0.8.19;
import {Script} from "forge-std/Script.sol";
import {Raffle} from "../src/Raffle.sol";
@@ -21,7 +21,7 @@ import {Raffle} from "../src/Raffle.sol";
Next, let's define a function called `deployContract` to handle the **deployment process**. This function will be similar to the one we used in the `FundMe` contract.
-```js
+```solidity
contract DeployRaffle is Script {
function run() external {
deployContract();
@@ -37,9 +37,9 @@ To deploy our contract, we need various parameters required by the `Raffle` cont
### The `HelperConfig.s.sol` Contract
-To retrieve the correct networ configuration, we can create a new file in the same directory called `HelperConfig.s.sol` and define a **Network Configuration Structure**:
+To retrieve the correct network configuration, we can create a new file in the same directory called `HelperConfig.s.sol` and define a **Network Configuration Structure**:
-```js
+```solidity
contract HelperConfig is Script {
struct NetworkConfig {
uint256 entranceFee;
@@ -54,14 +54,14 @@ contract HelperConfig is Script {
We'll then define two functions that return the _network-specific configuration_. We'll set up these functions for Sepolia and a local network.
-```js
+```solidity
function getSepoliaEthConfig() public pure returns (NetworkConfig memory) {
return NetworkConfig({
- entranceFee: 0.01 ether, //1e16
+ entranceFee: 0.01 ether, // 1e16
interval: 30, // 30 seconds
vrfCoordinator: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
- callbackGasLimit: 500000, //500,000 gas
+ callbackGasLimit: 500000, // 500,000 gas
subscriptionId: 0
});
}
@@ -78,9 +78,9 @@ function getLocalConfig() public pure returns (NetworkConfig memory) {
}
```
-We will then create an abstract contract `CodeConstants` where we define some network IDs. The `HelperConfig` contract will be able to use them later through ineritance.
+We will then create an abstract contract `CodeConstants` where we define some network IDs. The `HelperConfig` contract will be able to use them later through inheritance.
-```js
+```solidity
abstract contract CodeConstants {
uint256 public constant ETH_SEPOLIA_CHAIN_ID = 11155111;
uint256 public constant LOCAL_CHAIN_ID = 31337;
@@ -92,7 +92,7 @@ These values can be used inside the `HelperConfig` constructor:
> 👀❗**IMPORTANT**:br
> We are choosing the use of **constants** over magic numbers
-```js
+```solidity
constructor() {
networkConfigs[ETH_SEPOLIA_CHAIN_ID] = getSepoliaEthConfig();
}
@@ -100,7 +100,7 @@ constructor() {
We also have to build a function to fetch the appropriate configuration based on the actual chain ID. This can be done first by verifying that a VRF coordinator exists. In case it does not and we are not on a local chain, we'll revert.
-```js
+```solidity
function getConfigByChainId(uint256 chainId) public view returns (NetworkConfig memory) {
if (networkConfigs[chainId].vrfCoordinator != address(0)) {
return networkConfigs[chainId];
@@ -114,7 +114,7 @@ function getConfigByChainId(uint256 chainId) public view returns (NetworkConfig
In case we are on a local chain but the VRF coordinator has already been set, we should use the existing configuration already created.
-```js
+```solidity
function getOrCreateAnvilEthConfig() public returns (NetworkConfig memory) {
// Check to see if we set an active network config
if (localNetworkConfig.vrfCoordinator != address(0)) {
diff --git a/courses/foundry/4-smart-contract-lottery/21-deploy-a-mock-chainlink-vrf/+page.md b/courses/foundry/4-smart-contract-lottery/21-deploy-a-mock-chainlink-vrf/+page.md
index 3700215d9..da7c8d3df 100644
--- a/courses/foundry/4-smart-contract-lottery/21-deploy-a-mock-chainlink-vrf/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/21-deploy-a-mock-chainlink-vrf/+page.md
@@ -5,75 +5,80 @@ _Follow along with this video:_
---
-### Chainlink VRF mock contract
+### Chainlink VRF mock contract
A mock contract is a type of smart contract used in testing and development environments to simulate the behavior of real contracts. It allows us to create controlled and predictable scenarios for testing purposes without relying on actual external contracts or data sources. Moreover, it facilitates testing using Anvil, which is extremely fast and practical in comparison to a testnet.
In the last lesson, we stopped on `HelperConfig.s.sol`:
-```javascript
- function getOrCreateAnvilEthConfig()
- public
- returns (NetworkConfig memory anvilNetworkConfig)
- {
- // Check to see if we set an active network config
- if (activeNetworkConfig.vrfCoordinatorV2 != address(0)) {
- return activeNetworkConfig;
- }
+```solidity
+function getOrCreateAnvilEthConfig()
+ public
+ returns (NetworkConfig memory anvilNetworkConfig)
+{
+ // Check to see if we set an active network config
+ if (activeNetworkConfig.vrfCoordinator != address(0)) {
+ return activeNetworkConfig;
}
+}
```
We need to treat the other side of the `(activeNetworkConfig.vrfCoordinatorV2 != address(0))` condition. What happens if that is false?
-If that is false we need to deploy a mock vrfCoordinatorV2 and pass its address inside a `NetworkConfig` that will be used on Anvil.
+If that is false we need to deploy a mock vrfCoordinatorV2_5 and pass its address inside a `NetworkConfig` that will be used on Anvil.
Please use your Explorer on the left side to access the following path:
-`foundry-f23/foundry-smart-contract-lottery-f23/lib/chainlink/contracts/src/v0.8/vrf`
+`foundry-smart-contract-lottery-cu/lib/chainlink/contracts/src/v0.8/vrf/`
-Inside you'll find multiple folders, one of which is called `mocks`. Inside that folder, you can find the `VRFCoordinatorV2Mock` mock contract created by Chainlink.
+Inside you'll find multiple folders, one of which is called `mocks`. Inside that folder, you can find the `VRFCoordinatorV2_5Mock` mock contract created by Chainlink.
Add the following line in the imports section of `HelperConfig.s.sol`:
-```javascript
-import {VRFCoordinatorV2Mock} from "chainlink/src/v0.8/vrf/mocks/VRFCoordinatorV2Mock.sol";
+```solidity
+import {VRFCoordinatorV2_5Mock} from "chainlink/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";
```
Amazing! Now let's keep on working on the `getOrCreateAnvilEthConfig` function. We need to deploy the `vrfCoordinatorV2Mock`, but if we open it we'll see that its constructor requires some parameters:
-```javascript
- constructor(uint96 _baseFee, uint96 _gasPriceLink) ConfirmedOwner(msg.sender) {
- BASE_FEE = _baseFee;
- GAS_PRICE_LINK = _gasPriceLink;
- setConfig();
- }
+```solidity
+contract VRFCoordinatorV2_5Mock is SubscriptionAPI, IVRFCoordinatorV2Plus {
+ uint96 public immutable i_base_fee;
+ uint96 public immutable i_gas_price;
+ int256 public immutable i_wei_per_unit_link;
+}
```
-The `_baseFee` is the flat fee that VRF charges for the provided randomness and `_gasPriceLink` which is the gas consumed by the VRF node when calling your function. Given the way it's structured the callback gas is paid initially by the node which needs to be reimbursed. Both these parameters are denominated in LINK tokens.
+The `i_base_fee` is the flat fee that VRF charges for the provided randomness. `i_gas_price` which is the gas consumed by the VRF node when calling your function. `i_wei_per_unit_link` is the LINK price in ETH in wei units. Given the way it's structured the callback gas is paid initially by the node which needs to be reimbursed.
We add the following lines to the `getOrCreateAnvilEthConfig` function:
-```javascript
- uint96 baseFee = 0.25 ether; // To be understood as 0.25 LINK
- uint96 gasPriceLink = 1e9; // 1 gwei LINK
-
- vm.startBroadcast();
- VRFCoordinatorV2Mock vrfCoordinatorV2Mock = new VRFCoordinatorV2Mock(
- baseFee,
- gasPriceLink
- );
+```solidity
+/* VRF Mock Values */
+uint96 public constant MOCK_BASE_FEE = 0.25 ether;
+uint96 public constant MOCK_GAS_PRICE_LINK = 1e9;
+int256 public constant MOCK_WEI_PER_UNIT_LINK = 4e15;
+
+vm.startBroadcast();
+VRFCoordinatorV2_5Mock vrfCoordinatorMock = new VRFCoordinatorV2_5Mock(
+ MOCK_BASE_FEE,
+ MOCK_GAS_PRICE_LINK,
+ MOCK_WEI_PER_UNIT_LINK,
+);
+vm.stopBroadcast();
```
Amazing! Now that we have everything we need, let's perform the return, similar to what we did in `getSepoliaEthConfig`.
-```javascript
- return NetworkConfig({
- entranceFee: 0.01 ether,
- interval: 30, // 30 seconds
- vrfCoordinator: address(vrfCoordinatorV2Mock),
- gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
- subscriptionId: 0, // If left as 0, our scripts will create one!
- callbackGasLimit: 500000 // 500,000 gas
- });
+```solidity
+return NetworkConfig({
+ entranceFee: 0.01 ether,
+ interval: 30, // 30 seconds
+ vrfCoordinator: address(vrfCoordinatorMock),
+ // gasLane value doesn't matter.
+ gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
+ subscriptionId: 0,
+ callbackGasLimit: 500_000,
+});
```
-Great! Now this is fixed let's continue testing and deploying our Raffle contract.
\ No newline at end of file
+Great! Now this is fixed let's continue testing and deploying our Raffle contract.
diff --git a/courses/foundry/4-smart-contract-lottery/22-tests-and-deploy-the-lotterys-smart-contract-p2/+page.md b/courses/foundry/4-smart-contract-lottery/22-tests-and-deploy-the-lotterys-smart-contract-p2/+page.md
index 1925a3939..23a2d13a8 100644
--- a/courses/foundry/4-smart-contract-lottery/22-tests-and-deploy-the-lotterys-smart-contract-p2/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/22-tests-and-deploy-the-lotterys-smart-contract-p2/+page.md
@@ -24,7 +24,7 @@ Please create a new file called `DeployRaffle.s.sol` inside the `script` folder.
And now you know the drill, go write as much of it as you can! After you get stuck or after you finish come back and check it against the version below:
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
@@ -33,7 +33,7 @@ import {Raffle} from "../src/Raffle.sol";
contract DeployRaffle is Script {
- function run() external returns (Raffle){
+ function run() external returns (Raffle) {
}
}
@@ -46,7 +46,7 @@ Create a new file called `HelperConfig.s.sol` in the `script` folder.
Inside let's create the `HelperConfig` contract:
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
@@ -62,6 +62,8 @@ contract HelperConfig is Script {
uint64 subscriptionId;
uint32 callbackGasLimit;
}
+
+}
```
We start with the `SPDX `and `pragma solidity` declarations. Then, we import `Script` from Foundry, name the contract and make it inherit `Script`. Cool! Now what do we need to deploy the `Raffle` contract? That information can be easily found in the `Raffle` contract's constructor:
@@ -72,51 +74,51 @@ We created a new struct called `NetworkConfig` and we matched its contents with
Great! Now let's design a function that returns the proper config for Sepolia:
-```javascript
- function getSepoliaEthConfig()
- public
- pure
- returns (NetworkConfig memory)
- {
- return NetworkConfig({
- entranceFee: 0.01 ether,
- interval: 30, // 30 seconds
- vrfCoordinator: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
- gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
- subscriptionId: 0, // If left as 0, our scripts will create one!
- callbackGasLimit: 500000, // 500,000 gas
- });
- }
+```solidity
+function getSepoliaEthConfig()
+ public
+ pure
+ returns (NetworkConfig memory)
+{
+ return NetworkConfig({
+ entranceFee: 0.01 ether,
+ interval: 30, // 30 seconds
+ vrfCoordinator: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
+ gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
+ subscriptionId: 0, // If left as 0, our scripts will create one!
+ callbackGasLimit: 500000, // 500,000 gas
+ });
+}
```
-The function above returns a `NetworkConfig` struct with data taken from [here](https://docs.chain.link/vrf/v2/subscription/supported-networks#sepolia-testnet). The `interval`, `entranceFee` and `callbackGasLimit` were selected by Patrick.
+The function above returns a `NetworkConfig` struct with data taken from [here](https://docs.chain.link/vrf/v2-5/supported-networks#sepolia-testnet). The `interval`, `entranceFee` and `callbackGasLimit` were selected by Patrick.
Ok, we need a couple more things. We need a constructor that checks what blockchain we are on and attributes a state variable, let's call it `activeNetworkConfig`, the proper config for the chain used.
-```javascript
- NetworkConfig public activeNetworkConfig;
- constructor() {
- if (block.chainid == 11155111) {
- activeNetworkConfig = getSepoliaEthConfig();
- } else {
- activeNetworkConfig = getOrCreateAnvilEthConfig();
- }
+```solidity
+NetworkConfig public activeNetworkConfig;
+constructor() {
+ if (block.chainid == 11155111) {
+ activeNetworkConfig = getSepoliaEthConfig();
+ } else {
+ activeNetworkConfig = getOrCreateAnvilEthConfig();
}
+}
```
Good, we only missing the `getOrCreateAnvilEthConfig` function.
For now, let's create only a part of it:
-```javascript
- function getOrCreateAnvilEthConfig()
- public
- returns (NetworkConfig memory anvilNetworkConfig)
- {
- // Check to see if we set an active network config
- if (activeNetworkConfig.vrfCoordinator != address(0)) {
- return activeNetworkConfig;
- }
-
+```solidity
+function getOrCreateAnvilEthConfig()
+ public
+ returns (NetworkConfig memory anvilNetworkConfig)
+{
+ // Check to see if we set an active network config
+ if (activeNetworkConfig.vrfCoordinator != address(0)) {
+ return activeNetworkConfig;
+ }
+}
```
-We check if the `activeNetworkConfig` is populated, and if is we return it. If not we need to deploy some mocks. But more on that in the next lesson.
\ No newline at end of file
+We check if the `activeNetworkConfig` is populated, and if is we return it. If not we need to deploy some mocks. But more on that in the next lesson.
diff --git a/courses/foundry/4-smart-contract-lottery/23-setup-the-tests/+page.md b/courses/foundry/4-smart-contract-lottery/23-setup-the-tests/+page.md
index 17740fa26..33affbab5 100644
--- a/courses/foundry/4-smart-contract-lottery/23-setup-the-tests/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/23-setup-the-tests/+page.md
@@ -7,9 +7,9 @@ _Follow along with this video:_
### Continuing our testing journey
-Let's jump straight into testing! But ... where do we start?
+Let's jump straight into testing! But where do we start?
-Easy! Let's call `forge -coverage`:
+Easy! Let's call `forge coverage`:
```
Analysing contracts...
@@ -35,14 +35,14 @@ Patrick chose number 2. So what is the main entry point of our Raffle contract?
Let's look closely at it:
-```javascript
- function enterRaffle() external payable {
- if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
- if (s_raffleState != RaffleState.OPEN) revert Raffle__RaffleNotOpen();
+```solidity
+function enterRaffle() external payable {
+ if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
+ if (s_raffleState != RaffleState.OPEN) revert Raffle__RaffleNotOpen();
- s_players.push(payable(msg.sender));
- emit EnteredRaffle(msg.sender);
- }
+ s_players.push(payable(msg.sender));
+ emit EnteredRaffle(msg.sender);
+}
```
1. We check if the `msg.value` is high enough;
@@ -52,14 +52,14 @@ Let's look closely at it:
Let's test point 1:
-```javascript
- function testRaffleRevertsWHenYouDontPayEnough() public {
- // Arrange
- vm.prank(PLAYER);
- // Act / Assert
- vm.expectRevert(Raffle.Raffle__NotEnoughEthSent.selector);
- raffle.enterRaffle();
- }
+```solidity
+function testRaffleRevertsWHenYouDontPayEnough() public {
+ // Arrange
+ vm.prank(PLAYER);
+ // Act / Assert
+ vm.expectRevert(Raffle.Raffle__NotEnoughEthSent.selector);
+ raffle.enterRaffle();
+}
```
We call `vm.prank(PLAYER)` to configure the fact that the next transaction will be called by the `PLAYER`. [Refresher](https://book.getfoundry.sh/cheatcodes/prank?highlight=prank#prank)
@@ -78,23 +78,23 @@ We will skip point 2 for now, let's go straight to point 3:
But before being able to test if a player is properly recorded in the `s_players` array we first need a view function to access the players in the `s_players`:
-```javascript
- function getPlayer(uint256 index) public view returns (address) {
- return s_players[index];
- }
+```solidity
+function getPlayer(uint256 index) public view returns (address) {
+ return s_players[index];
+}
```
Now that we have all the tools we need:
-```javascript
- function testRaffleRecordsPlayerWhenTheyEnter() public {
- // Arrange
- vm.prank(PLAYER);
- // Act
- raffle.enterRaffle{value: entranceFee}();
- // Assert
- address playerRecorded = raffle.getPlayer(0);
- assert(playerRecorded == PLAYER);
- }
+```solidity
+function testRaffleRecordsPlayerWhenTheyEnter() public {
+ // Arrange
+ vm.prank(PLAYER);
+ // Act
+ raffle.enterRaffle{value: entranceFee}();
+ // Assert
+ address playerRecorded = raffle.getPlayer(0);
+ assert(playerRecorded == PLAYER);
+}
```
We start by pranking the PLAYER, then properly calling the `enterRaffle` function specifying the correct `value`. We call the new `getPLayer` function to copy the player recorded at index 0 in memory. Then we compare that value to the `PLAYER` address to ensure they match.
diff --git a/courses/foundry/4-smart-contract-lottery/25-adding-more-tests/+page.md b/courses/foundry/4-smart-contract-lottery/25-adding-more-tests/+page.md
index 56305379f..1412e15e5 100644
--- a/courses/foundry/4-smart-contract-lottery/25-adding-more-tests/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/25-adding-more-tests/+page.md
@@ -11,18 +11,18 @@ Welcome back! Let's continue testing our `Raffle` contract.
We should test if the check upkeep returns false if the contract has no balance. Open your `RaffleTest.t.sol` and write the following:
-```javascript
- function testCheckUpkeepReturnsFalseIfItHasNoBalance() public {
- // Arrange
- vm.warp(block.timestamp + interval + 1);
- vm.roll(block.number + 1);
-
- // Act
- (bool upkeepNeeded, ) = raffle.checkUpkeep("");
-
- // Assert
- assert(!upkeepNeeded);
- }
+```solidity
+function testCheckUpkeepReturnsFalseIfItHasNoBalance() public {
+ // Arrange
+ vm.warp(block.timestamp + interval + 1);
+ vm.roll(block.number + 1);
+
+ // Act
+ (bool upkeepNeeded, ) = raffle.checkUpkeep("");
+
+ // Assert
+ assert(!upkeepNeeded);
+}
```
We use `warp` and `roll` to set the `block.timestamp` in the future. We call `checkUpkeep` and record its return in memory. We check it returned `false`.
@@ -35,21 +35,21 @@ It passes, amazing!
What else? We should test if the check upkeep function returns false if the raffle is not Open. Paste the following inside `RaffleTest.t.sol`:
-```javascript
- function testCheckUpkeepReturnsFalseIfRaffleIsntOpen() public {
- // Arrange
- vm.prank(PLAYER);
- raffle.enterRaffle{value: entranceFee}();
- vm.warp(block.timestamp + interval + 1);
- vm.roll(block.number + 1);
- raffle.performUpkeep("");
- Raffle.RaffleState raffleState = raffle.getRaffleState();
- // Act
- (bool upkeepNeeded, ) = raffle.checkUpkeep("");
- // Assert
- assert(raffleState == Raffle.RaffleState.CALCULATING);
- assert(upkeepNeeded == false);
- }
+```solidity
+function testCheckUpkeepReturnsFalseIfRaffleIsntOpen() public {
+ // Arrange
+ vm.prank(PLAYER);
+ raffle.enterRaffle{value: entranceFee}();
+ vm.warp(block.timestamp + interval + 1);
+ vm.roll(block.number + 1);
+ raffle.performUpkeep("");
+ Raffle.RaffleState raffleState = raffle.getRaffleState();
+ // Act
+ (bool upkeepNeeded, ) = raffle.checkUpkeep("");
+ // Assert
+ assert(raffleState == Raffle.RaffleState.CALCULATING);
+ assert(upkeepNeeded == false);
+}
```
We start by pranking the `PLAYER`. Then we enter the `raffle` using the correct `entranceFee`. After that, we use `warp` and `roll` to set `block.timestamp` in the future. We call `performUpkeep`. This will modify the `RaffleState` into `CALCULATING`. We then call `checkUpkeep` and record its return in memory. We check it returned `false`. We also check that the `RaffleState` is indeed `CALCULATING`.
@@ -91,7 +91,7 @@ Uncovered for src/Raffle.sol:
You can follow the locations indicated to find the lines not covered by tests. For example, in my `Raffle.sol` the code block starting on line 97 is this:
-```javascript
+```solidity
function performUpkeep(bytes calldata /* performData */) external override {
(bool upkeepNeeded, ) = checkUpkeep("");
// require(upkeepNeeded, "Upkeep not needed");
@@ -110,4 +110,4 @@ But beware! This is not entirely accurate. For example, `checkUpkeep` doesn't ap
Try writing these two tests yourself and then compare them against what [Patrick wrote](https://github.com/Cyfrin/foundry-smart-contract-lottery-f23/blob/d106fe245e0e44239dae2479b63545351ed1236a/test/unit/RaffleTest.t.sol).
-Great job! Let's keep going!
\ No newline at end of file
+Great job! Let's keep going!
diff --git a/courses/foundry/4-smart-contract-lottery/26-testing-events/+page.md b/courses/foundry/4-smart-contract-lottery/26-testing-events/+page.md
index 9b03e6e89..e3b4a060e 100644
--- a/courses/foundry/4-smart-contract-lottery/26-testing-events/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/26-testing-events/+page.md
@@ -21,22 +21,22 @@ So, inside `RaffleTest.t.sol` declare the following event:
Then we proceed to the test:
-```javascript
- function testEmitsEventOnEntrance() public {
- // Arrange
- vm.prank(PLAYER);
-
- // Act / Assert
- vm.expectEmit(true, false, false, false, address(raffle));
- emit EnteredRaffle(PLAYER);
- raffle.enterRaffle{value: entranceFee}();
- }
+```solidity
+function testEmitsEventOnEntrance() public {
+ // Arrange
+ vm.prank(PLAYER);
+
+ // Act / Assert
+ vm.expectEmit(true, false, false, false, address(raffle));
+ emit EnteredRaffle(PLAYER);
+ raffle.enterRaffle{value: entranceFee}();
+}
```
-- We prank the `PLAYER`;
+- We prank the `PLAYER`
- We call the `expectEmit` cheatcode - `vm.expectEmit(true, false, false, false, address(raffle));`
I know this looks a bit weird. But let's look at what `expectEmit` expects:
- ```javascript
+ ```solidity
function expectEmit(
bool checkTopic1,
bool checkTopic2,
diff --git a/courses/foundry/4-smart-contract-lottery/27-using-vm.roll-and-vm.wrap/+page.md b/courses/foundry/4-smart-contract-lottery/27-using-vm.roll-and-vm.wrap/+page.md
index 05b9d537c..7a917d698 100644
--- a/courses/foundry/4-smart-contract-lottery/27-using-vm.roll-and-vm.wrap/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/27-using-vm.roll-and-vm.wrap/+page.md
@@ -7,24 +7,24 @@ _Follow along with this video:_
### vm.roll and vm.wrap
-In Lesson 19 we skipped testing one of the four steps of `enterRaffle`: ```2. We check if the `RaffleState` is `OPEN`;```
+In lesson 19, we skipped testing one of the four steps of `enterRaffle`: ```2. We check if the `RaffleState` is `OPEN`;```
To rephrase it, a user should not be able to enter if the `RaffleState` is `CALCULATING`.
-```javascript
- function testDontAllowPlayersToEnterWhileRaffleIsCalculating() public {
- // Arrange
- vm.prank(PLAYER);
- raffle.enterRaffle{value: entranceFee}();
- vm.warp(block.timestamp + interval + 1);
- vm.roll(block.number + 1);
- raffle.performUpkeep("");
-
- // Act / Assert
- vm.expectRevert(Raffle.Raffle__RaffleNotOpen.selector);
- vm.prank(PLAYER);
- raffle.enterRaffle{value: entranceFee}();
- }
+```solidity
+function testDontAllowPlayersToEnterWhileRaffleIsCalculating() public {
+ // Arrange
+ vm.prank(PLAYER);
+ raffle.enterRaffle{value: entranceFee}();
+ vm.warp(block.timestamp + interval + 1);
+ vm.roll(block.number + 1);
+ raffle.performUpkeep("");
+
+ // Act / Assert
+ vm.expectRevert(Raffle.Raffle__RaffleNotOpen.selector);
+ vm.prank(PLAYER);
+ raffle.enterRaffle{value: entranceFee}();
+}
```
We start our test exactly like the others. We `prank` the `PLAYER` and we call `enterRaffle` specifying the appropriate `msg.value` so our user registers properly.
@@ -56,4 +56,4 @@ Ran 1 test for test/unit/RaffleTest.t.sol:RaffleTest
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 2.70ms (206.20µs CPU time)
```
-OH NO! `[FAIL. Reason: InvalidConsumer()]` ... we gonna fix this one soon, I promise!
\ No newline at end of file
+OH NO! `[FAIL. Reason: InvalidConsumer()]` ... we gonna fix this one soon, I promise!
diff --git a/courses/foundry/4-smart-contract-lottery/28-subscribing-to-events/+page.md b/courses/foundry/4-smart-contract-lottery/28-subscribing-to-events/+page.md
index 06619e8de..6ea638690 100644
--- a/courses/foundry/4-smart-contract-lottery/28-subscribing-to-events/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/28-subscribing-to-events/+page.md
@@ -25,13 +25,13 @@ It looks like when we call `performUpkeep` it internally calls `requestRandomWor
Go to `HelperConfig.s.sol` and try to follow the path of the `VRFCoordinatorV2Mock`. Inside we can see why our function failed:
-```javascript
- modifier onlyValidConsumer(uint64 _subId, address _consumer) {
+```solidity
+modifier onlyValidConsumer(uint64 _subId, address _consumer) {
if (!consumerIsAdded(_subId, _consumer)) {
- revert InvalidConsumer();
+ revert InvalidConsumer();
}
_;
- }
+}
```
@@ -49,69 +49,68 @@ Inside the `script` folder create a new file called `Interactions.sol`. This is
Let's start with the basics:
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script} from "forge-std/Script.sol";
contract CreateSubscription is Script {
-
+
}
```
Every script needs a `run` function. Inside the `run` function we will call the `createSubscriptionUsingConfig`.
-```javascript
- function createSubscriptionUsingConfig() public returns (uint64) {
-
- }
+```solidity
+function createSubscriptionUsingConfig() public returns (uint64) {
+}
- function run() external returns (uint64) {
- return createSubscriptionUsingConfig();
- }
+function run() external returns (uint64) {
+ return createSubscriptionUsingConfig();
+}
```
Let's pause and talk about what we are doing and what we need to make things happen. Thinking back about what we did in Lesson 6. We created a subscription, we added a consumer and we funded the subscription. Open the `VRFCoordinatorV2Mock` and let's look for functions that we need to do it programmatically:
-```javascript
- function createSubscription() external override returns (uint64 _subId) {
+```solidity
+function createSubscription() external override returns (uint64 _subId) {
s_currentSubId++;
s_subscriptions[s_currentSubId] = Subscription({owner: msg.sender, balance: 0});
emit SubscriptionCreated(s_currentSubId, msg.sender);
return s_currentSubId;
- }
+}
[...]
- function addConsumer(uint64 _subId, address _consumer) external override onlySubOwner(_subId) {
+function addConsumer(uint64 _subId, address _consumer) external override onlySubOwner(_subId) {
if (s_consumers[_subId].length == MAX_CONSUMERS) {
- revert TooManyConsumers();
+ revert TooManyConsumers();
}
-
+
if (consumerIsAdded(_subId, _consumer)) {
- return;
+ return;
}
s_consumers[_subId].push(_consumer);
emit ConsumerAdded(_subId, _consumer);
- }
+}
[...]
- function fundSubscription(uint64 _subId, uint96 _amount) public {
+function fundSubscription(uint64 _subId, uint96 _amount) public {
if (s_subscriptions[_subId].owner == address(0)) {
- revert InvalidSubscription();
+ revert InvalidSubscription();
}
uint96 oldBalance = s_subscriptions[_subId].balance;
s_subscriptions[_subId].balance += _amount;
emit SubscriptionFunded(_subId, oldBalance, oldBalance + _amount);
- }
+}
```
Great! Now we need to call all of them, but before that, we first need to pull the VRFv2 address, available in the `HelperConfig`.
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
@@ -149,7 +148,7 @@ As we said above, we created a `run` function that calls `createSubscriptionUsin
Amazing! Let's work on the `createSubscription` function. We need to import some things to make it work. First, let's update the contract in order to import `console`, to log a message every time we create a subscription. Second, let's import the `VRFCoordinatorV2Mock` to be able to call the functions we specified above.
-```javascript
+```solidity
import {Script, console} from "forge-std/Script.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
import {VRFCoordinatorV2Mock} from "chainlink/src/v0.8/vrf/mocks/VRFCoordinatorV2Mock.sol";
@@ -157,25 +156,25 @@ import {VRFCoordinatorV2Mock} from "chainlink/src/v0.8/vrf/mocks/VRFCoordinatorV
Perfect, not let's finish the `createSubscription`:
-```javascript
- function createSubscription(
- address vrfCoordinator
- ) public returns (uint64) {
- console.log("Creating subscription on ChainID: ", block.chainid);
- vm.startBroadcast();
- uint64 subId = VRFCoordinatorV2Mock(vrfCoordinator).createSubscription();
- vm.stopBroadcast();
- console.log("Your sub Id is: ", subId);
- console.log("Please update subscriptionId in HelperConfig!");
- return subId;
- }
+```solidity
+function createSubscription(
+ address vrfCoordinator
+) public returns (uint64) {
+ console.log("Creating subscription on ChainID: ", block.chainid);
+ vm.startBroadcast();
+ uint64 subId = VRFCoordinatorV2Mock(vrfCoordinator).createSubscription();
+ vm.stopBroadcast();
+ console.log("Your sub Id is: ", subId);
+ console.log("Please update subscriptionId in HelperConfig!");
+ return subId;
+}
```
First, we log the `Creating subscription` message. Then, we encapsulate the `VRFCoordinatorV2Mock(vrfCoordinator).createSubscription();` call inside the `vm.startBroadcast` and `vm.stopBroadcast` block. We assign the return of the `VRFCoordinatorV2Mock(vrfCoordinator).createSubscription` call to `uint64 subId` variable. Then we log the `subId` and return it to end the function.
Amazing work! Coming back to `DeployRaffle.s.sol`, we should create a subscription if we don't have one, like this:
-```javascript
+```solidity
import {Script} from "forge-std/Script.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
import {Raffle} from "../src/Raffle.sol";
@@ -218,4 +217,3 @@ contract DeployRaffle is Script {
We import the newly created `CreateSubscription` contract from `Interactions.s.sol`. After the `helperConfig` definition, we check if our `subscriptionId` is 0. If that yields true then we don't have a `subscriptionId` and we need to create one. We use the new functions inside the `CreateSubscription` to get an appropriate `subscriptionId`.
Amazing work!
-
diff --git a/courses/foundry/4-smart-contract-lottery/3-solidity-style-guide/+page.md b/courses/foundry/4-smart-contract-lottery/3-solidity-style-guide/+page.md
index 9830bac4c..2e480e1b0 100644
--- a/courses/foundry/4-smart-contract-lottery/3-solidity-style-guide/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/3-solidity-style-guide/+page.md
@@ -7,20 +7,22 @@ _Follow along with this video:_
### Solidity style guide
-In the previous section we talked a bit about Solidity's style guide, code layouts and naming conventions. However, it's intriguing to note that we didn't fully explore how to properly order our Solidity functions. Solidity docs provide an `Order of Layout`:
+In the previous section we talked a bit about Solidity's style guide, code layouts and naming conventions. However, it's intriguing to note that we didn't fully explore how to properly order our Solidity functions. Solidity docs provide an [Order of Layout](https://docs.soliditylang.org/en/latest/style-guide.html#order-of-layout):
-```javascript
+```solidity
// Layout of the contract file:
// version
// imports
// errors
// interfaces, libraries, contract
+
// Inside Contract:
// Type declarations
// State variables
// Events
// Modifiers
// Functions
+
// Layout of Functions:
// constructor
// receive function (if exists)
@@ -29,8 +31,7 @@ In the previous section we talked a bit about Solidity's style guide, code layou
// public
// internal
// private
-// internal & private view & pure functions
-// external & public view & pure functions
+// view & pure functions
```
-Sometimes it's useful to paste this as a comment at the top of your contract to always remember it!
\ No newline at end of file
+Sometimes it's useful to paste this as a comment at the top of your contract to always remember it!
diff --git a/courses/foundry/4-smart-contract-lottery/30-fund-subscription/+page.md b/courses/foundry/4-smart-contract-lottery/30-fund-subscription/+page.md
index b99270c8c..ee0c686d8 100644
--- a/courses/foundry/4-smart-contract-lottery/30-fund-subscription/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/30-fund-subscription/+page.md
@@ -1,395 +1,281 @@
-<<<<<<< HEAD:courses/foundry/4-smart-contract-lottery/24-fund-subscription/+page.md
----
-title: Fund subscription
-
-_Follow along with this video:_
-
----
-
-### Funding a subscription programmatically
-
-In the previous lessons, we learned how to create a subscription using both the Chainlink UI and programmatically. Let's see how we can fund the subscription programmatically.
-
-This is what the subscription creation snippet from `DeployRaffle` looks like:
-
-```javascript
- if (subscriptionId == 0) {
- CreateSubscription createSubscription = new CreateSubscription();
- subscriptionId = createSubscription.createSubscription(vrfCoordinator);
- }
-```
-
-Below the `subscriptionId` line, we need to continue with the funding logic.
-
-For that let's open the `Interactions.s.sol` and below the existing contract create another contract called `FundSubscription`:
-
-```javascript
-contract FundSubscription is Script {
- uint96 public constant FUND_AMOUNT = 3 ether;
-
- function fundSubscriptionUsingConfig() public {
-
- }
-
- function run() external {
- fundSubscriptionUsingConfig();
- }
-}
-```
-
-I know this step looks very similar to what we did in the subscription creation lesson. That is completely fine and desirable!
-
-One thing we need and we currently don't have configured is the LINK token. If you remember in the previous lesson we funded our subscription with LINK, and we need to do the same thing here.
-
-What do we need:
-
-1. Sepolia testnet has already a LINK contract deployed, we need to have that address easily accessible inside our `HelperConfiguration`. To always make sure you get the correct LINK contract access the following [link](https://docs.chain.link/resources/link-token-contracts?parent=vrf).
-2. Anvil doesn't come with a LINK contract deployed. We need to deploy a mock LINK token contract and use it to fund our subscription.
-
-Let's start modifying our `HelperConfig.s.sol`:
-
-```javascript
- struct NetworkConfig {
- uint256 entranceFee;
- uint256 interval;
- address vrfCoordinator;
- bytes32 gasLane;
- uint64 subscriptionId;
- uint32 callbackGasLimit;
- address link;
- }
-[...]
-
- function getSepoliaEthConfig()
- public
- view
- returns (NetworkConfig memory)
- {
- return NetworkConfig({
- entranceFee: 0.01 ether,
- interval: 30, // 30 seconds
- vrfCoordinator: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
- gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
- subscriptionId: 0, // If left as 0, our scripts will create one!
- callbackGasLimit: 500000, // 500,000 gas
- link: 0x779877A7B0D9E8603169DdbD7836e478b4624789
- });
- }
-```
-
-We've added the LINK address in the `NetworkConfig` struct and hardcoded it in the `getSepoliaEthConfig` function. This modification also requires some adjustments in the `Interactions.s.sol`:
-
-Now the fun part! Patrick conveniently provided us with a mock LINK token contract. You can access it [here](https://github.com/Cyfrin/foundry-smart-contract-lottery-f23/blob/d106fe245e0e44239dae2479b63545351ed1236a/test/mocks/LinkToken.sol).
-
-Inside the `test` folder create a new folder called `mocks`. Inside that create a new file called `LinkToken.sol`. Copy Patrick's contract in the new file. Looking through it we can see that it imports ERC20 from a library called Solmate which self-describes itself as a `Modern, opinionated, and gas optimized building blocks for smart contract development`. We need to install it with the following command:
-
-`forge install transmissions11/solmate --no-commit`
-
-Add the following line inside your `remappings.txt`:
-
-`@solmate/=lib/solmate/src`
-
-Back in our `HelperConfig.s.sol` we need to import the LinkToken:
-
-```javascript
-import {LinkToken} from "../test/mocks/LinkToken.sol";
-```
-
-And now, with this new import, we can deploy the token in case we use Anvil like so:
-
-```javascript
-
- function getOrCreateAnvilEthConfig()
- public
- returns (NetworkConfig memory anvilNetworkConfig)
- {
- // Check to see if we set an active network config
- if (activeNetworkConfig.vrfCoordinator != address(0)) {
- return activeNetworkConfig;
- }
- uint96 baseFee = 0.25 ether;
- uint96 gasPriceLink = 1e9;
-
- vm.startBroadcast();
- VRFCoordinatorV2Mock vrfCoordinatorV2Mock = new VRFCoordinatorV2Mock(
- baseFee,
- gasPriceLink
- );
- LinkToken link = new LinkToken();
- vm.stopBroadcast();
-
- return NetworkConfig({
- entranceFee: 0.01 ether,
- interval: 30, // 30 seconds
- vrfCoordinator: address(vrfCoordinatorV2Mock),
- gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
- subscriptionId: 0, // If left as 0, our scripts will create one!
- callbackGasLimit: 500000, // 500,000 gas
- link: address(link)
- });
- }
-```
-
-Amazing work!
-
-Now we need to look through our code and make sure we have enough elements everywhere we use the `NetworkConfig` struct, which got upgraded from 6 elements to 7 elements because we've added the link address.
-
-Normal people don't remember all the places, and that's alright. Run `forge build`.
-
-It should look something like this:
-
-```
-[⠒] Solc 0.8.24 finished in 1.34s
-Error:
-Compiler run failed:
-Error (7364): Different number of components on the left hand side (6) than on the right hand side (7).
- --> script/DeployRaffle.s.sol:12:9:
- |
-12 | (
- | ^ (Relevant source part starts here and spans across multiple lines).
-
-Error (7407): Type tuple(uint256,uint256,address,bytes32,uint64,uint32,address) is not implicitly convertible to expected type tuple(uint256,uint256,address,bytes32,uint64,uint32).
- --> test/unit/RaffleTest.t.sol:42:13:
- |
-42 | ) = helperConfig.activeNetworkConfig();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-```
-
-Even if this looks scary, it tells you where you need to perform the changes.
-
-Control + Click the paths (`script/DeployRaffle.s.sol:12:9:`) to go to the broken code and fix it by making sure it takes the newly added `address link` parameter.
-
-Inside the `Raffle.t.sol` make sure to define the `address link` in the state variables section. Then add it in here as well:
-
-```javascript
- (
- entranceFee,
- interval,
- vrfCoordinator,
- gasLane,
- subscriptionId,
- callbackGasLimit,
- link
-
- ) = helperConfig.activeNetworkConfig();
-```
-
-Adjust the `helperConfig.activeNetworkConfig` inside the `DeployRaffle.s.sol` as well:
-
-```javascript
- (
- uint256 entranceFee,
- uint256 interval,
- address vrfCoordinator,
- bytes32 gasLane,
- uint64 subscriptionId,
- uint32 callbackGasLimit,
- address link
- ) = helperConfig.activeNetworkConfig();
-```
-
-Take care of both the places where we call `activeNetworkConfig` inside `Interactions.s.sol`:
-
-```javascript
- function createSubscriptionUsingConfig() public returns (uint64) {
- HelperConfig helperConfig = new HelperConfig();
- (
- ,
- ,
- address vrfCoordinator,
- ,
- ,
- ,
- ) = helperConfig.activeNetworkConfig();
-
- return createSubscription(vrfCoordinator);
- }
-```
-
-```javascript
- function fundSubscriptionUsingConfig() public {
- HelperConfig helperConfig = new HelperConfig();
- (
- ,
- ,
- address vrfCoordinator,
- ,
- uint64 subscriptionId,
- ,
- address link
- ) = helperConfig.activeNetworkConfig();
- }
-```
-
-Try another `forge build`. This time it compiled on my side, but if it didn't compile on your side just keep control clicking through the errors and fixing them. If you get stuck please come on Cifryns Discord in the Updraft section and ask for help.
-
-Great! Now our script uses the right LINK address when we work on Sepolia and deploys a new LinkToken when we work on Anvil.
-
-Let's come back to `Interactions.s.sol` and finish our `fundSubscription`:
-
-```javascript
-contract FundSubscription is Script {
- uint96 public constant FUND_AMOUNT = 3 ether;
-
- function fundSubscriptionUsingConfig() public {
- HelperConfig helperConfig = new HelperConfig();
- (
- ,
- ,
- address vrfCoordinator,
- ,
- uint64 subscriptionId,
- ,
- address link
- ) = helperConfig.activeNetworkConfig();
- fundSubscription(vrfCoordinator, subscriptionId, link);
- }
-
- function fundSubscription(address vrfCoordinator, uint64 subscriptionId, address link) public {
- console.log("Funding subscription: ", subscriptionId);
- console.log("Using vrfCoordinator: ", vrfCoordinator);
- console.log("On ChainID: ", block.chainid);
- if (block.chainid == 31337) {
- vm.startBroadcast();
- VRFCoordinatorV2Mock(vrfCoordinator).fundSubscription(subscriptionId, FUND_AMOUNT);
- vm.stopBroadcast();
- } else {
- vm.startBroadcast();
- LinkToken(link).transferAndCall(vrfCoordinator, FUND_AMOUNT, abi.encode(subscriptionId));
- vm.stopBroadcast();
- }
- }
-
- function run() external {
- fundSubscriptionUsingConfig();
- }
-```
-
-This seems like a lot, but it isn't, let's go through it step by step:
-
-- Like any other Script our's has a `run` function that gets executed;
-- Inside we call the `fundSubscriptionUsingConfig` function;
-- Inside the `fundSubscriptionUsingConfig` function we get the `activeNetworkConfig` that provides the chain-appropriate `vrfCoordinator`, `subscriptionId` and `link` token address;
-- At the end of `fundSubscriptionUsingConfig` we call the `fundSubscription`, a function that we are going to define;
-- We define `fundSubscription` as a public function that takes the 3 parameters as input;
-- We console log some details, this will help us debug down the road;
-- Then using an `if` statement we check if we are using Anvil, if that's the case we'll use the `fundSubscription` method found inside the `VRFCoordinatorV2Mock`;
-- If we are not using Anvil, it means we are using Sepolia. The way we fund the Sepolia `vrfCoordinator` is by using the LINK's `transferAndCall` function.
-
-**Note:** The `transferAndCall` function is part of the `ERC-677 standard`, which extends the `ERC-20` token standard by adding the ability to execute a function call in the recipient contract immediately after transferring tokens. This feature is particularly useful in scenarios where you want to atomically transfer tokens and trigger logic in the receiving contract within a single transaction, enhancing efficiency and reducing the risk of reentrancy attacks. In the context of Chainlink, the LINK token implements the `transferAndCall` function. When a smart contract wants to request data from a Chainlink oracle, it uses this function to send LINK tokens to the oracle's contract address while simultaneously encoding the request details in the _data parameter. The oracle's contract then decodes this data to understand what service is being requested.
-
-Don't worry! You'll get enough opportunities to understand these on the way to becoming the greatest solidity dev/auditor!
-
-For now, let's run a `forge build`. Everything compiles, great!
-
-Take a break and continue watching Patrick running the newly created script to fund the subscription he created via the UI in the past lesson.
-
-=======
---
-title: Fund Subscriptions
+title: Fund subscription
+
+_Follow along with this video:_
+
---
-_Follow along with this lesson and watch the video below:_
+### Funding a subscription programmatically
+In the previous lessons, we learned how to create a subscription using both the Chainlink UI and programmatically. Let's see how we can fund the subscription programmatically.
+This is what the subscription creation snippet from `DeployRaffle` looks like:
----
+```solidity
+if (subscriptionId == 0) {
+ CreateSubscription createSubscription = new CreateSubscription();
+ (subscriptionId, vrfCoordinator) = createSubscription.createSubscription(vrfCoordinator);
+}
+```
+
+Below the `subscriptionId` line, we need to continue with the funding logic.
-## Creating a New Contract
+For that let's open the `Interactions.s.sol` and below the existing contract create another contract called `FundSubscription`:
-First things first. Head over to the Interactions section, and create a new contract, named `FundSubscription`. This contract script, residing within `interactions.s.sol`, will allow you to select an amount and fund your subscription.
+```solidity
+contract FundSubscription is Script, CodeConstants {
+ uint256 public constant FUND_AMOUNT = 3 ether;
-Remember, the amount has to be a `uint96` , but let's keep things simple for now and set a public constant `FUND_AMOUNT` to three ether.
+ function fundSubscriptionUsingConfig() public {
+ }
-```js
-uint96 public constant FUND_AMOUNT = 3 ether;
+ function run() external {
+ fundSubscriptionUsingConfig();
+ }
+}
```
-## Setting the Parameters
+I know this step looks very similar to what we did in the subscription creation lesson. That is completely fine and desirable!
-To fund your subscription, you will need three important elements:
+One thing we need and we currently don't have configured is the LINK token. If you remember in the previous lesson, we funded our subscription with LINK, and we need to do the same thing here.
-- Subscription ID
-- VRF Coordinator V2 address
-- Link address
+What do we need:
->UPDATE: on the recent versions of Chainlink VRF, the subscription ID is a uint256 instead of a uint64.
+1. Sepolia testnet has already a LINK contract deployed, we need to have that address easily accessible inside our `HelperConfiguration`. To always make sure you get the correct LINK contract access the following [link](https://docs.chain.link/resources/link-token-contracts?parent=vrf).
+2. Anvil doesn't come with a LINK contract deployed. We need to deploy a mock LINK token contract and use it to fund our subscription.
+Let's start modifying our `HelperConfig.s.sol`:
-Start by specifying the `VRFCoordinator` address and the `uint64` `subId`. The `subID` corresponds to the subscription you want to fund.
+```solidity
+ struct NetworkConfig {
+ uint256 entranceFee;
+ uint256 interval;
+ address vrfCoordinator;
+ bytes32 gasLane;
+ uint64 subscriptionId;
+ uint32 callbackGasLimit;
+ address linkToken;
+ }
+[...]
-```js
-HelperConfig helperConfig = new HelperConfig();
- (
- uint64 subId,
- ,
- ,
- ,
- ,
- address vrfCoordinatorV2,
- address link,
- uint256 deployerKey
- ) = helperConfig.activeNetworkConfig();
+ function getSepoliaEthConfig()
+ public
+ view
+ returns (NetworkConfig memory)
+ {
+ return NetworkConfig({
+ entranceFee: 0.01 ether,
+ interval: 30, // 30 seconds
+ vrfCoordinator: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
+ gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
+ subscriptionId: 0, // If left as 0, our scripts will create one!
+ callbackGasLimit: 500000, // 500,000 gas
+ link: 0x779877A7B0D9E8603169DdbD7836e478b4624789
+ });
+ }
```
-For these configurations, you'll use the already existing `HelperConfig.s.sol`. However, you'll notice, it doesn't yet include a link token. Adding a link token will facilitate funding the subscription as it forms the basis of the contract call.
+We've added the LINK address in the `NetworkConfig` struct and hardcoded it in the `getSepoliaEthConfig` function. This modification also requires some adjustments in the `Interactions.s.sol`:
-The link tokens for Sepolia already exist, and they can be easily found and added.
+Now the fun part! Patrick conveniently provided us with a mock LINK token contract. You can access it [here](https://github.com/Cyfrin/foundry-smart-contract-lottery-cu/blob/efac6e2a5c2df6d1936d117f40f93575d25cf694/test/mocks/LinkToken.sol).
-Next, for Anvil, you'll need to deploy a mock link token. To ease the process, simply rewrite the link contract for a newer version of Solidity. This can be easily done using my Foundry smart contract lottery F23.
+Inside the `test` folder, create a new folder called `mocks`. Inside that, create a new file called `LinkToken.sol`. Copy Patrick's contract in the new file. Looking through it, we can see that it imports ERC20 from a library called Solmate which self-describes itself as a `Modern, opinionated, and gas optimized building blocks for smart contract development`. We need to install it with the following command:
-## Funding the Subscription
+`forge install transmissions11/solmate --no-commit`
-Now that the `link_address` is ready, go back to your interactions and create a new function named `fund_subscription`. The function should have three inputs: `VRF_Coordinator`, `sub_ID`, and `link`.
+Add the following line inside your `remappings.txt`:
-```js
-contract FundSubscription is Script {
- uint96 public constant FUND_AMOUNT = 3 ether;
+`@solmate/=lib/solmate/src`
- function fundSubscriptionUsingConfig() public {
- HelperConfig helperConfig = new HelperConfig();
- (
- uint64 subId,
- ,
- ,
- ,
- ,
- address vrfCoordinatorV2,
- address link,
- uint256 deployerKey
- ) = helperConfig.activeNetworkConfig();
- fundSubscription(vrfCoordinatorV2, subId, link, deployerKey);
+Back in our `HelperConfig.s.sol` we need to import the LinkToken:
+
+```solidity
+import {LinkToken} from "test/mocks/LinkToken.sol";
+```
+
+And now, with this new import, we can deploy the token in case we use Anvil like so:
+
+```solidity
+function getOrCreateAnvilEthConfig() internal returns (NetworkConfig memory) {
+ // Check to see if we set an active network config
+ if (localNetworkConfig.vrfCoordinator != address(0)) {
+ return localNetworkConfig;
}
+
+ // Deploy mocks, etc
+ vm.startBroadcast();
+ VRFCoordinatorV2_5Mock vrfCoordinatorMock = new VRFCoordinatorV2_5Mock(
+ MOCK_BASE_FEE,
+ MOCK_GAS_PRICE_LINK,
+ MOCK_WEI_PER_UNIT_LINK
+ );
+ LinkToken linkToken = new LinkToken();
+ vm.stopBroadcast();
+
+ localNetworkConfig = NetworkConfig({
+ entranceFee: 0.01 ether,
+ interval: 30, // 30 seconds
+ vrfCoordinator: address(vrfCoordinatorMock),
+ // Doesn't matter for the gasLane value
+ gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
+ subscriptionId: 0,
+ callbackGasLimit: 500_000,
+ linkToken: address(linkToken)
+ });
+
+ return localNetworkConfig;
+}
```
-This function works in much the same way as the front-end does to fund subscriptions. However, remember that the VRF Coordinator Mock interacts with the link token transfers in a different way than the actual contract, hence the mock's funding subscription mechanism is different.
+Amazing work!
-When you're testing your code on your local chain, you can call the `VM_Start_Broadcast` function before and `VM_Stop_Broadcast` function after the line of code which contains the `fundSubscriptionUsingConfig` method.
+Now we need to look through our code and make sure we have enough fields everywhere we use the `NetworkConfig` struct, which increased from 6 fields to 7 fields because we've added the link address.
-```js
-if (subscriptionId == 0) {
- CreateSubscription createSubscription = new CreateSubscription();
- subscriptionId = createSubscription.createSubscription(
- vrfCoordinatorV2,
- deployerKey
- );
-
- FundSubscription fundSubscription = new FundSubscription();
- fundSubscription.fundSubscription(
- vrfCoordinatorV2,
- subscriptionId,
- link,
- deployerKey
- );
+Most people don't remember all the places, and that's alright. Run `forge build`.
+
+It should look something like this:
+
+```
+[⠒] Solc 0.8.24 finished in 1.34s
+Error:
+Compiler run failed:
+Error (7364): Different number of components on the left hand side (6) than on the right hand side (7).
+ --> script/DeployRaffle.s.sol:12:9:
+ |
+12 | (
+ | ^ (Relevant source part starts here and spans across multiple lines).
+
+Error (7407): Type tuple(uint256,uint256,address,bytes32,uint64,uint32,address) is not implicitly convertible to expected type tuple(uint256,uint256,address,bytes32,uint64,uint32).
+ --> test/unit/RaffleTest.t.sol:42:13:
+ |
+42 | ) = helperConfig.localNetworkConfig();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+```
+
+Even if this looks scary, it tells you where you need to perform the changes.
+
+Control + Click the paths (`script/DeployRaffle.s.sol:12:9:`) to go to the broken code and fix it by making sure it takes the newly added `address linkToken` parameter.
+
+Inside the `Raffle.t.sol` make sure to define the `address linkToken` in the state variables section. Then add it in here as well:
+
+```solidity
+HelperConfig.NetworkConfig memory config = helperConfig.getConfig();
+entranceFee = config.entranceFee;
+interval = config.interval;
+vrfCoordinator = config.vrfCoordinator;
+gasLane = config.gasLane;
+subscriptionId = config.subscriptionId;
+callbackGasLimit = config.callbackGasLimit;
+linkToken = LinkToken(config.linkToken);
+```
+
+And then, import our mock Link token contract:
+```solidity
+import {LinkToken} from "test/mocks/LinkToken.sol";
+```
+
+Take care of both the places where we call `HelperConfig()` to set our config inside `Interactions.s.sol`:
+
+```solidity
+function fundSubscriptionUsingConfig() public {
+ HelperConfig helperConfig = new HelperConfig();
+ address vrfCoordinator = helperConfig.getConfig().vrfCoordinator;
+ uint256 subscriptionId = helperConfig.getConfig().subscriptionId;
+ address linkToken = helperConfig.getConfig().linkToken;
+
+ fundSubscription(vrfCoordinator, subscriptionId, linkToken);
+}
+```
+
+```solidity
+function fundSubscription(address vrfCoordinator, uint256 subscriptionId, address linkToken) public {
+ console.log("Funding subscription: ", subscriptionId);
+ console.log("Using vrfCoordinator: ", vrfCoordinator);
+ console.log("On chainId: ", block.chainid);
+
+ if(block.chainid == ETH_ANVIL_CHAIN_ID) {
+ vm.startBroadcast();
+ VRFCoordinatorV2_5Mock(vrfCoordinator).fundSubscription(subscriptionId, FUND_AMOUNT);
+ vm.stopBroadcast();
+ } else {
+ console.log(LinkToken(linkToken).balanceOf(msg.sender));
+ console.log(msg.sender);
+ console.log(LinkToken(linkToken).balanceOf(address(this)));
+ console.log(address(this));
+ vm.startBroadcast();
+ LinkToken(linkToken).transferAndCall(vrfCoordinator, FUND_AMOUNT, abi.encode(subscriptionId));
+ vm.stopBroadcast();
+ }
+}
+```
+
+Try another `forge build`. This time it compiled on my side, but if it didn't compile on your side just keep control clicking through the errors and fixing them. If you get stuck please come on Cyfrin Discord in the Updraft section and ask for help.
+
+Great! Now our script uses the right LINK address when we work on Sepolia, and deploys a new LinkToken when we work on Anvil.
+
+Let's come back to `Interactions.s.sol` and finish our `FundSubscription` contract:
+```solidity
+contract FundSubscription is Script, CodeConstants {
+ uint256 public constant FUND_AMOUNT = 3 ether;
+
+ function fundSubscriptionUsingConfig() public {
+ HelperConfig helperConfig = new HelperConfig();
+ address vrfCoordinator = helperConfig.getConfig().vrfCoordinator;
+ uint256 subscriptionId = helperConfig.getConfig().subscriptionId;
+ address linkToken = helperConfig.getConfig().linkToken;
+
+ if (subscriptionId == 0) {
+ CreateSubscription createSub = new CreateSubscription();
+ (uint256 updatedSubId, address updatedVRFv2) = createSub.run();
+ subscriptionId = updatedSubId;
+ vrfCoordinator = updatedVRFv2;
+ console.log("New SubId Created! ", subscriptionId, "VRF Address: ", vrfCoordinator);
+ }
+
+ fundSubscription(vrfCoordinator, subscriptionId, linkToken);
+ }
+
+ function fundSubscription(address vrfCoordinator, uint256 subscriptionId, address linkToken) public {
+ console.log("Funding subscription: ", subscriptionId);
+ console.log("Using vrfCoordinator: ", vrfCoordinator);
+ console.log("On chainId: ", block.chainid);
+
+ if(block.chainid == ETH_ANVIL_CHAIN_ID) {
+ vm.startBroadcast();
+ VRFCoordinatorV2_5Mock(vrfCoordinator).fundSubscription(subscriptionId, FUND_AMOUNT);
+ vm.stopBroadcast();
+ } else {
+ console.log(LinkToken(linkToken).balanceOf(msg.sender));
+ console.log(msg.sender);
+ console.log(LinkToken(linkToken).balanceOf(address(this)));
+ console.log(address(this));
+ vm.startBroadcast();
+ LinkToken(linkToken).transferAndCall(vrfCoordinator, FUND_AMOUNT, abi.encode(subscriptionId));
+ vm.stopBroadcast();
}
+ }
+ function run() public {
+ fundSubscriptionUsingConfig();
+ }
+}
```
-Finally, compile all the contracts using forge build. If everything compiles successfully, your contract has been created and is ready to perform transactions!
+This seems like a lot, but it isn't, let's go through it step by step:
+
+- Like any other Script our's has a `run` function that gets executed
+- Inside we call the `fundSubscriptionUsingConfig` function
+- Inside the `fundSubscriptionUsingConfig` function we get the `activeNetworkConfig` that provides the chain-appropriate `vrfCoordinator`, `subscriptionId` and `link` token address
+- At the end of `fundSubscriptionUsingConfig` we call the `fundSubscription`, a function that we are going to define
+- We define `fundSubscription` as a public function that takes the 3 parameters as input
+- We console log some details, this will help us debug down the road
+- Then using an `if` statement we check if we are using Anvil, if that's the case we'll use the `fundSubscription` method found inside the `VRFCoordinatorV2_5Mock`
+- If we are not using Anvil, it means we are using Sepolia. The way we fund the Sepolia `vrfCoordinator` is by using the LINK's `transferAndCall` function.
+
+**Note:** The `transferAndCall` function is part of the `ERC-677 standard`, which extends the `ERC-20` token standard by adding the ability to execute a function call in the recipient contract immediately after transferring tokens. This feature is particularly useful in scenarios where you want to atomically transfer tokens and trigger logic in the receiving contract within a single transaction, enhancing efficiency and reducing the risk of reentrancy attacks. In the context of Chainlink, the LINK token implements the `transferAndCall` function. When a smart contract wants to request data from a Chainlink oracle, it uses this function to send LINK tokens to the oracle's contract address while simultaneously encoding the request details in the _data parameter. The oracle's contract then decodes this data to understand what service is being requested.
-## A Final Comment
+Don't worry! You'll get enough opportunities to understand these on the way to becoming the greatest Solidity dev/auditor!
-The above steps outline a process whereby you can automate the process of funding blockchain-based subscriptions. Remember, this is not the final product, but an intermediary step in the development of a blockchain-based subscription service. Please do not use this code in a production environment without further testing and validation.
+For now, let's run a `forge build`. Everything compiles, great!
-Remember, it's always better to test your code in a secure environment before deploying it. The world of coding is vast, and there's so much more to explore. Happy coding!
->>>>>>> 062bb989bf82b83cba0d590d03ef08654e1ba1cc:courses/foundry/4-smart-contract-lottery/30-fund-subscription/+page.md
+Take a break and continue watching Patrick running the newly created script to fund the subscription he created via the UI in the past lesson.
diff --git a/courses/foundry/4-smart-contract-lottery/31-adding-a-consumer/+page.md b/courses/foundry/4-smart-contract-lottery/31-adding-a-consumer/+page.md
index 19d6d32f6..659438588 100644
--- a/courses/foundry/4-smart-contract-lottery/31-adding-a-consumer/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/31-adding-a-consumer/+page.md
@@ -11,7 +11,7 @@ Remember how everything started from a simple and inoffensive `InvalidConsumer()
Open `Interactions.s.sol` and create a new contract:
-```javascript
+```solidity
contract AddConsumer is Script {
function run() external {
@@ -29,7 +29,7 @@ Import it at the top of the `Interactions.s.sol`:
Update the `run` function to get the address and call `addConsumerUsingConfig(raffle)`:
-```javascript
+```solidity
function run() external {
address raffle = DevOpsTools.get_most_recent_deployment("MyContract", block.chainid);
addConsumerUsingConfig(raffle);
@@ -38,7 +38,7 @@ Update the `run` function to get the address and call `addConsumerUsingConfig(ra
And right about now, everything should feel extremely familiar. Let's define `addConsumerUsingConfig` and all the rest:
-```javascript
+```solidity
contract AddConsumer is Script {
function addConsumer(address raffle, address vrfCoordinator, uint64 subscriptionId) public {
@@ -91,7 +91,7 @@ Let's go back to `DeployRaffle.s.sol` and import the thing we added in `Interact
Now let's integrate the `FundSubscription` with the `CreateSubscription` bit:
-```javascript
+```solidity
if (subscriptionId == 0) {
CreateSubscription createSubscription = new CreateSubscription();
subscriptionId = createSubscription.createSubscription(vrfCoordinator);
@@ -129,4 +129,3 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 11.06ms (102.80µs
Amazing work!
There is a lot more to do in this section, but you are a true hero for reaching this point, take a well-deserved break! See you in the next one!
-
diff --git a/courses/foundry/4-smart-contract-lottery/32-even-more-tests/+page.md b/courses/foundry/4-smart-contract-lottery/32-even-more-tests/+page.md
index 8f030507b..140ddb517 100644
--- a/courses/foundry/4-smart-contract-lottery/32-even-more-tests/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/32-even-more-tests/+page.md
@@ -19,7 +19,7 @@ To improve our coverage, we need to write additional tests. For example we can a
1. Let’s start by ensuring that `checkUpkeep` returns `false` when there is no balance. We’ll do this by setting up our test environment similarly to previous tests but without entering the raffle. Here’s the code:
- ```js
+ ```solidity
function testCheckUpkeepReturnsFalseIfItHasNoBalance() public {
// Arrange
vm.warp(block.timestamp + automationUpdateInterval + 1);
@@ -30,11 +30,11 @@ To improve our coverage, we need to write additional tests. For example we can a
// Assert
assert(!upkeepNeeded);
- }
+ }
```
2. Next, we want to assert that `checkUpkeep` returns `false` when the raffle is in a _not open_ state. To do this, we can use a setup similar to our previous test:
- ```js
+ ```solidity
function testCheckUpkeepReturnsFalseIfRaffleIsntOpen() public {
// Arrange
vm.prank(PLAYER);
@@ -43,12 +43,14 @@ To improve our coverage, we need to write additional tests. For example we can a
vm.roll(block.number + 1);
raffle.performUpkeep("");
Raffle.RaffleState raffleState = raffle.getRaffleState();
+
// Act
(bool upkeepNeeded,) = raffle.checkUpkeep("");
+
// Assert
assert(raffleState == Raffle.RaffleState.CALCULATING);
assert(upkeepNeeded == false);
- }
+ }
```
### Conclusion
diff --git a/courses/foundry/4-smart-contract-lottery/34-testing-and-refactoring-the-performupkeep/+page.md b/courses/foundry/4-smart-contract-lottery/34-testing-and-refactoring-the-performupkeep/+page.md
index 0d14859d5..b26f5a847 100644
--- a/courses/foundry/4-smart-contract-lottery/34-testing-and-refactoring-the-performupkeep/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/34-testing-and-refactoring-the-performupkeep/+page.md
@@ -11,18 +11,18 @@ Let's give some love to `performUpkeep`, starting with some tests.
Starting light, open the `RaffleTest.t.sol` and paste the following:
-```javascript
- function testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue() public {
- // Arrange
- vm.prank(PLAYER);
- raffle.enterRaffle{value: entranceFee}();
- vm.warp(block.timestamp + interval + 1);
- vm.roll(block.number + 1);
-
- // Act / Assert
- // It doesnt revert
- raffle.performUpkeep("");
- }
+```solidity
+function testPerformUpkeepCanOnlyRunIfCheckUpkeepIsTrue() public {
+ // Arrange
+ vm.prank(PLAYER);
+ raffle.enterRaffle{value: entranceFee}();
+ vm.warp(block.timestamp + interval + 1);
+ vm.roll(block.number + 1);
+
+ // Act / Assert
+ // It doesnt revert
+ raffle.performUpkeep("");
+}
```
We prank the `PLAYER` address, then use it to call `enterRaffle` with the correct `entranceFee`. We use the `warp` and `roll` to set `block.timestamp` into the future. Lastly, we call `performUpkeep`.
@@ -33,30 +33,30 @@ Run the test using: `forge test --mt testPerformUpkeepCanOnlyRunIfCheckUpkeepIsT
It passes, amazing!
-Keep going! Let's test if perform upkeep reverts in case check upkeep is false:
-
-```javascript
- function testPerformUpkeepRevertsIfCheckUpkeepIsFalse() public {
- // Arrange
- uint256 currentBalance = 0;
- uint256 numPlayers = 0;
- Raffle.RaffleState rState = raffle.getRaffleState();
- // Act / Assert
- vm.expectRevert(
- abi.encodeWithSelector(
- Raffle.Raffle__UpkeepNotNeeded.selector,
- currentBalance,
- numPlayers,
- rState
- )
- );
- raffle.performUpkeep("");
- }
+Keep going! Let's test if `performUpkeep` reverts in case `checkUpkeep` is false:
+
+```solidity
+function testPerformUpkeepRevertsIfCheckUpkeepIsFalse() public {
+ // Arrange
+ uint256 currentBalance = 0;
+ uint256 numPlayers = 0;
+ Raffle.RaffleState rState = raffle.getRaffleState();
+ // Act / Assert
+ vm.expectRevert(
+ abi.encodeWithSelector(
+ Raffle.Raffle__UpkeepNotNeeded.selector,
+ currentBalance,
+ numPlayers,
+ rState
+ )
+ );
+ raffle.performUpkeep("");
+}
```
This can be understood easier if we start from the end. We want to call `performUpkeep` and we expect it to revert. For that, we use the `vm.expectRevert` to indicate that we expect the next call to revert. If we access [this link](https://book.getfoundry.sh/cheatcodes/expect-revert) we can see that in case we use a custom error with parameters we can specify them as follows:
-```javascript
+```solidity
vm.expectRevert(
abi.encodeWithSelector(CustomError.selector, 1, 2)
);
@@ -64,12 +64,12 @@ vm.expectRevert(
In our case the custom error has 3 parameters:
-```javascript
- error Raffle__UpkeepNotNeeded(
- uint256 currentBalance,
- uint256 numPlayers,
- uint256 raffleState
- );
+```solidity
+error Raffle__UpkeepNotNeeded(
+ uint256 currentBalance,
+ uint256 numPlayers,
+ uint256 raffleState
+);
```
First parameter: `Raffle.Raffle__UpkeepNotNeeded.selector`;
@@ -85,4 +85,4 @@ Everything passes, great!
I know some concepts haven't been explained. I'm referring to `encodeWithSelector` and the general concept of function selectors. These will be introduced in the next sections.
-Great work! Now let's further explore events.
\ No newline at end of file
+Great work! Now let's further explore events.
diff --git a/courses/foundry/4-smart-contract-lottery/35-refactoring-events-data/+page.md b/courses/foundry/4-smart-contract-lottery/35-refactoring-events-data/+page.md
index a85ad6fa7..8f86e0750 100644
--- a/courses/foundry/4-smart-contract-lottery/35-refactoring-events-data/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/35-refactoring-events-data/+page.md
@@ -8,38 +8,38 @@ _Follow along with this video:_
### Refactoring events data
In this lesson, we will learn how to access event data inside our tests.
-
Let's create a new event and emit it in `performUpkeep` to test something.
-
Inside `Raffle.sol` in the events section create a new event:
-
`event RequestedRaffleWinner(uint256 indexed requestId);`
-
Emit the event at the end of the `performUpkeep` function:
-```javascript
-
- function performUpkeep(bytes calldata /* performData */) external override {
- (bool upkeepNeeded, ) = checkUpkeep("");
- // require(upkeepNeeded, "Upkeep not needed");
- if (!upkeepNeeded) {
- revert Raffle__UpkeepNotNeeded(
- address(this).balance,
- s_players.length,
- uint256(s_raffleState)
- );
- }
- s_raffleState = RaffleState.CALCULATING;
- uint256 requestId = i_vrfCoordinator.requestRandomWords(
- i_gasLane,
- i_subscriptionId,
- REQUEST_CONFIRMATIONS,
- i_callbackGasLimit,
- NUM_WORDS
+```solidity
+function performUpkeep(bytes calldata /* performData */) external override {
+ (bool upkeepNeeded, ) = checkUpkeep("");
+ // require(upkeepNeeded, "Upkeep not needed");
+ if (!upkeepNeeded) {
+ revert Raffle__UpkeepNotNeeded(
+ address(this).balance,
+ s_players.length,
+ uint256(s_raffleState)
);
-
- emit RequestedRaffleWinner(requestId);
}
+ s_raffleState = RaffleState.CALCULATING;
+ VRFV2PlusClient.RandomWordsRequest memory request = VRFV2PlusClient.RandomWordsRequest({
+ keyHash: i_keyHash,
+ subId: i_subscriptionId,
+ requestConfirmations: REQUEST_CONFIRMATIONS,
+ callbackGasLimit: i_callbackGasLimit,
+ numWords: NUM_WORDS,
+ extraArgs: VRFV2PlusClient._argsToBytes(
+ // Set nativePayment to true to pay for VRF requests with Sepolia ETH instead of LINK
+ VRFV2PlusClient.ExtraArgsV1({nativePayment: false})
+ )
+ });
+ uint256 requestId = s_vrfCoordinator.requestRandomWords(request);
+
+ emit RequestedRaffleWinner(requestId);
+}
```
At this point in the video, Patrick asks the audience if this event is redundant. This is an amazing question to ask yourself every time you do something in Solidity because as you know, everything costs gas. Another absolute truth about this environment is that no one wants to pay gas. So we, as developers, need to write efficient code.
@@ -56,30 +56,29 @@ Add `import {Vm} from "forge-std/Vm.sol";` inside the import sections of `Raffle
We decided to include the `PLAYER` entering the raffle and setting `block.timestamp` into the future inside a modifier. That way we can easily use that everywhere, without typing the same 4 rows of code over and over again.
-```javascript
- modifier raffleEntredAndTimePassed() {
- vm.prank(PLAYER);
- raffle.enterRaffle{value: entranceFee}();
- vm.warp(block.timestamp + interval + 1);
- vm.roll(block.number + 1);
- _
- }
-
-
- function testPerformUpkeepUpdatesRaffleStateAndEmitsRequestId() public raffleEntredAndTimePassed {
-
- // Act
- vm.recordLogs();
- raffle.performUpkeep(""); // emits requestId
- Vm.Log[] memory entries = vm.getRecordedLogs();
- bytes32 requestId = entries[1].topics[1];
-
- // Assert
- Raffle.RaffleState raffleState = raffle.getRaffleState();
- // requestId = raffle.getLastRequestId();
- assert(uint256(requestId) > 0);
- assert(uint(raffleState) == 1); // 0 = open, 1 = calculating
- }
+```solidity
+modifier raffleEntredAndTimePassed() {
+ vm.prank(PLAYER);
+ raffle.enterRaffle{value: entranceFee}();
+ vm.warp(block.timestamp + interval + 1);
+ vm.roll(block.number + 1);
+ _
+}
+
+
+function testPerformUpkeepUpdatesRaffleStateAndEmitsRequestId() public raffleEntredAndTimePassed {
+ // Act
+ vm.recordLogs();
+ raffle.performUpkeep(""); // emits requestId
+ Vm.Log[] memory entries = vm.getRecordedLogs();
+ bytes32 requestId = entries[1].topics[1];
+
+ // Assert
+ Raffle.RaffleState raffleState = raffle.getRaffleState();
+ // requestId = raffle.getLastRequestId();
+ assert(uint256(requestId) > 0);
+ assert(uint(raffleState) == 1); // 0 = open, 1 = calculating
+}
```
Let's analyze the test line by line. We start by calling `vm.recordLogs()`. You can read more about this one [here](https://book.getfoundry.sh/cheatcodes/record-logs). This cheatcode starts recording all emitted events inside an array. After that, we call `performUpkeep` which emits both the events we talked earlier about. We can access the array where all the emitted events were stored by using `vm.getRecordedLogs()`. It usually takes some trial and error, or `forge debug` to know where the event that interests us is stored. But we can cheat a little bit. We know that the big event from the vrfCoordinator is emitted first, so our event is second, i.e. entries[1] (because the index starts from 0). Looking further in the examples provided [here](entries[1]), we see that the first topic, stored at index 0, is the name and output of the event. Given that our event only emits one parameter, the `requestId`, then we are aiming for `entries[1].topics[1]`.
@@ -89,4 +88,3 @@ Moving on, we get the raffle state using the `getRaffleState` view function. We
Run the test using `forge test --mt testPerformUpkeepUpdatesRaffleStateAndEmitsRequestId`.
It passes, great job!
-
diff --git a/courses/foundry/4-smart-contract-lottery/36-intro-to-fuzz-testing/+page.md b/courses/foundry/4-smart-contract-lottery/36-intro-to-fuzz-testing/+page.md
index ec8ec904d..a1a876cec 100644
--- a/courses/foundry/4-smart-contract-lottery/36-intro-to-fuzz-testing/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/36-intro-to-fuzz-testing/+page.md
@@ -5,57 +5,57 @@ _Follow along with this video:_
---
-### Fuzz testing
+### Fuzz testing
Generally, fuzz testing, also known as fuzzing, is an automated software testing technique that involves injecting invalid, malformed, or unexpected inputs into a system to identify software defects and vulnerabilities. This method helps in revealing issues that may lead to crashes, security breaches, or performance problems. Fuzz testing operates by feeding a program with large volumes of random data (referred to as "fuzz") to observe how the system handles such inputs. If the system crashes or exhibits abnormal behavior, it indicates a potential vulnerability or defect that needs to be addressed.
How can we apply this in Foundry?
-Let's find out by testing the fact that fulfillRandomWords can only be called after the upkeep is performed.
+Let's find out by testing the fact that `fulfillRandomWords` can only be called after the upkeep is performed.
Open `RaffleTest.t.sol` and add the following:
-`import {VRFCoordinatorV2Mock} from "chainlink/src/v0.8/vrf/mocks/VRFCoordinatorV2Mock.sol";` in the import section.
-
-```javascript
- function testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep()
- public
- raffleEntredAndTimePassed
- {
- // Arrange
- // Act / Assert
- vm.expectRevert("nonexistent request");
- // vm.mockCall could be used here...
- VRFCoordinatorV2Mock(vrfCoordinatorV2).fulfillRandomWords(
- 0,
- address(raffle)
- );
-
- }
+`import {VRFCoordinatorV2_5Mock} from "chainlink/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";` in the import section.
+
+```solidity
+function testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep()
+ public
+ raffleEntredAndTimePassed
+{
+ // Arrange
+ // Act / Assert
+ vm.expectRevert("nonexistent request");
+ // vm.mockCall could be used here...
+ VRFCoordinatorV2_5Mock(vrfCoordinator).fulfillRandomWords(
+ 0,
+ address(raffle)
+ );
+
+}
```
So we define the function and use the modifier we created in the previous lesson to make `PLAYER` enter the raffle and set `block.timestamp` into the future. We use the `expectRevert` because we expect the next call to revert with the `"nonexistent request"` message. How do we know that? Simple, inside the `VRFCoordinatorV2Mock` we can see the following code:
-```javascript
- function fulfillRandomWords(uint256 _requestId, address _consumer) external nonReentrant {
- fulfillRandomWordsWithOverride(_requestId, _consumer, new uint256[](0));
- }
-
- /**
- * @notice fulfillRandomWordsWithOverride allows the user to pass in their own random words.
- *
- * @param _requestId the request to fulfill
- * @param _consumer the VRF randomness consumer to send the result to
- * @param _words user-provided random words
- */
- function fulfillRandomWordsWithOverride(uint256 _requestId, address _consumer, uint256[] memory _words) public {
- uint256 startGas = gasleft();
- if (s_requests[_requestId].subId == 0) {
- revert("nonexistent request");
- }
+```solidity
+function fulfillRandomWords(uint256 _requestId, address _consumer) external nonReentrant {
+fulfillRandomWordsWithOverride(_requestId, _consumer, new uint256[](0));
+}
+
+/**
+* @notice fulfillRandomWordsWithOverride allows the user to pass in their own random words.
+*
+* @param _requestId the request to fulfill
+* @param _consumer the VRF randomness consumer to send the result to
+* @param _words user-provided random words
+*/
+function fulfillRandomWordsWithOverride(uint256 _requestId, address _consumer, uint256[] memory _words) public {
+uint256 startGas = gasleft();
+if (s_requests[_requestId].subId == 0) {
+ revert("nonexistent request");
+}
```
If the `requestId` is not registered, then the `if (s_requests[_requestId].subId == 0)` check would revert using the desired message.
@@ -64,23 +64,22 @@ Moving on, we called `vm.expectRevert` then we called `fulfillRandomWords` with
Here comes Foundry fuzzing:
-```javascript
- function testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep(uint256 requestId)
- public
- raffleEntredAndTimePassed
- {
- // Arrange
- // Act / Assert
- vm.expectRevert("nonexistent request");
- // vm.mockCall could be used here...
- VRFCoordinatorV2Mock(vrfCoordinator).fulfillRandomWords(
- requestId,
- address(raffle)
- );
- }
+```solidity
+function testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep(uint256 randomRequestId)
+ public
+ raffleEntredAndTimePassed
+{
+ // Arrange
+ // Act / Assert
+ vm.expectRevert("nonexistent request");
+ // vm.mockCall could be used here...
+ VRFCoordinatorV2_5Mock(vrfCoordinator).fulfillRandomWords(
+ randomRequestId,
+ address(raffle)
+ );
+}
```
If we specify an input parameter in the test function declaration, Foundry will provide random values wherever we use that parameter inside our test function.
This was just a small taste. Foundry fuzzing has an enormous testing capability. We will discuss more about them in the next sections.
-
diff --git a/courses/foundry/4-smart-contract-lottery/37-one-big-test/+page.md b/courses/foundry/4-smart-contract-lottery/37-one-big-test/+page.md
index ea8104817..438800fc6 100644
--- a/courses/foundry/4-smart-contract-lottery/37-one-big-test/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/37-one-big-test/+page.md
@@ -11,22 +11,21 @@ You are a true hero for reaching this lesson! Let's finalize the testing with a
Up until now, we've tested parts of the contract with a focus on checks that should revert in certain conditions. We never fully tested the happy case. We will do that now.
-Open your `RafleTest.t.sol` and add the following function:
+Open your `RaffleTest.t.sol` and add the following function:
-```javascript
- function testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney() public raffleEntredAndTimePassed {
- // Arrange
+```solidity
+function testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney() public raffleEnteredAndTimePassed {
+ // Arrange
- uint256 additionalEntrants = 5;
- uint256 startingIndex = 1;
+ uint256 additionalEntrants = 3;
+ uint256 startingIndex = 1;
- for (uint256 i = startingIndex; i < startingIndex + additionalEntrants; i++) {
- address player = address(uint160(i));
- hoax(player, 1 ether);
- raffle.enterRaffle{value:entranceFee}();
- }
+ for (uint256 i = startingIndex; i < startingIndex + additionalEntrants; i++) {
+ address player = address(uint160(i));
+ hoax(player, 1 ether);
+ raffle.enterRaffle{value: entranceFee}();
}
-
+}
```
This is not the whole function but we break it to not overwhelm you.
@@ -39,92 +38,96 @@ This is not the whole function but we break it to not overwhelm you.
Ok, now we need to pretend to be Chainlink VRF and call `fulfillRandomWords`. We will need the `requestId` and the `consumer`. The consumer is simple, it's the address of the `Raffle` contract. How do we get the `requestId`? We did this in the previous lesson!
-```javascript
- function testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney() public raffleEntredAndTimePassed {
- // Arrange
-
- uint256 additionalEntrants = 5;
- uint256 startingIndex = 1;
-
- for (uint256 i = startingIndex; i < startingIndex + additionalEntrants; i++) {
- address player = address(uint160(i));
- hoax(player, 1 ether);
- raffle.enterRaffle{value:entranceFee}();
- }
-
- vm.recordLogs();
- raffle.performUpkeep(""); // emits requestId
- Vm.Log[] memory entries = vm.getRecordedLogs();
- bytes32 requestId = entries[1].topics[1];
+```solidity
+function testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney() public raffleEnteredAndTimePassed {
+ // Arrange
- // pretend to be Chainlink VRF
- VRFCoordinatorV2Mock(vrfCoordinator).fulfillRandomWords(
- uint256(requestId),
- address(raffle)
- );
+ uint256 additionalEntrants = 333;
+ uint256 startingIndex = 1;
+ for (uint256 i = startingIndex; i < startingIndex + additionalEntrants; i++) {
+ address player = address(uint160(i));
+ hoax(player, 1 ether);
+ raffle.enterRaffle{value: entranceFee}();
}
+ vm.recordLogs();
+ raffle.performUpkeep(""); // emits requestId
+ Vm.Log[] memory entries = vm.getRecordedLogs();
+ bytes32 requestId = entries[1].topics[1];
+
+ // Pretend to be Chainlink VRF
+ VRFCoordinatorV2_5Mock(vrfCoordinator).fulfillRandomWords(
+ uint256(requestId),
+ address(raffle)
+ );
+}
```
6. We copy what we did in the previous lesson to get the `requestId` emitted by the `performUpkeep`;
-7. We then use the `VRFCoordinatorV2Mock` to call the `fulfillRandomWords` function. This is usually called by the Chainlink nodes but given that we are on our local Anvil chain we need to do that action.
+7. We then use the `VRFCoordinatorV2_5Mock` to call the `fulfillRandomWords` function. This is usually called by the Chainlink nodes but given that we are on our local Anvil chain we need to do that action.
8. `fulfillRandomWords` expects a uint256 `requestId`, so we use `uint256(requestId)` to cast it from `bytes32` to the expected type.
With this last call we've finished the `Arrange` stage of our test. Let's continue with the `Assert` stage.
Before that, we need a couple of view functions in `Raffle.sol`:
-```javascript
- function getRecentWinner() public view returns (address) {
- return s_recentWinner;
- }
+```solidity
+function getRecentWinner() public view returns (address) {
+ return s_recentWinner;
+}
- function getNumberOfPlayers() public view returns (uint256) {
- return s_players.length;
- }
+function getNumberOfPlayers() public view returns (uint256) {
+ return s_players.length;
+}
- function getLastTimeStamp() public view returns (uint256) {
- return s_lastTimeStamp;
- }
+function getLastTimeStamp() public view returns (uint256) {
+ return s_lastTimeStamp;
+}
```
We'll use this one in testing the recent winner.
-```javascript
- function testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney() public raffleEntredAndTimePassed {
- // Arrange
-
- uint256 additionalEntrants = 5;
- uint256 startingIndex = 1;
+```solidity
+function testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney() public raffleEnteredAndTimePassed {
+ // Arrange
- for (uint256 i = startingIndex; i < startingIndex + additionalEntrants; i++) {
- address player = address(uint160(i));
- hoax(player, STARTING_USER_BALANCE);
- raffle.enterRaffle{value:entranceFee}();
- }
+ uint256 additionalEntrants = 3;
+ uint256 startingIndex = 1;
- uint256 prize = entranceFee * (additionalEntrants + 1);
-
- vm.recordLogs();
- raffle.performUpkeep(""); // emits requestId
- Vm.Log[] memory entries = vm.getRecordedLogs();
- bytes32 requestId = entries[1].topics[1];
-
- uint256 previousTimeStamp = raffle.getLastTimeStamp();
-
- // pretend to be Chainlink VRF
- VRFCoordinatorV2Mock(vrfCoordinator).fulfillRandomWords(
- uint256(requestId),
- address(raffle)
- );
-
- assert(uint256(raffle.getRaffleState()) == 0);
- assert(raffle.getRecentWinner() != address(0));
- assert(raffle.getNumberOfPlayers() == 0);
- assert(raffle.getLastTimeStamp() > previousTimeStamp);
- assert(raffle.getRecentWinner().balance == STARTING_USER_BALANCE + prize);
+ for (uint256 i = startingIndex; i < startingIndex + additionalEntrants; i++) {
+ address player = address(uint160(i));
+ hoax(player, STARTING_USER_BALANCE);
+ raffle.enterRaffle{value: entranceFee}();
}
+
+ uint256 prize = entranceFee * (additionalEntrants + 1);
+ uint256 winnerStartingBalance = expectedWinner.balance;
+
+ // Act
+ vm.recordLogs();
+ raffle.performUpkeep(""); // emits requestId
+ Vm.Log[] memory entries = vm.getRecordedLogs();
+ bytes32 requestId = entries[1].topics[1];
+
+ // Pretend to be Chainlink VRF
+ VRFCoordinatorV2_5Mock(vrfCoordinator).fulfillRandomWords(
+ uint256(requestId),
+ address(raffle)
+ );
+
+ // Assert
+ address recentWinner = raffle.getRecentWinner();
+ Raffle.RaffleState raffleState = raffle.getRaffleState();
+ uint256 winnerBalance = recentWinner.balance;
+ uint256 endingTimeStamp = raffle.getLastTimeStamp();
+ uint256 prize = entranceFee * (additionalEntrants + 1);
+
+ assert(expectedWinner == recentWinner);
+ assert(uint256(raffleState) == 0);
+ assert(winnerBalance == winnerStartingBalance + prize);
+ assert(endingTimeStamp > startingTimeStamp);
+}
```
9. We've made some changes. Whenever you test things make sure to be consistent and try to avoid using magic numbers. We hoaxed the newly created addresses with `1 ether` for no obvious reason. We should use the `STARTING_USER_BALANCE` for consistency.
@@ -138,7 +141,7 @@ Now we are ready to start our assertions.
14. We assert that the `fullfillRandomWords` updates the `s_lastTimeStamp` variable;
15. We assert that the winner receives their ETH prize;
-Amazing work, let's try it out with `forge test --mt testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney --vv`.
+Amazing work, let's try it out with `forge test --mt testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney --vv`.
Aaaaand it failed:
@@ -147,81 +150,51 @@ Ran 1 test suite in 2.35s (11.16ms CPU time): 0 tests passed, 1 failed, 0 skippe
Failing tests:
Encountered 1 failing test in test/unit/RaffleTest.t.sol:RaffleTest
-[FAIL. Reason: panic: assertion failed (0x01)] testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney() (gas: 362400)
+[FAIL. Reason: InvalidRequest()] testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney() (gas: 362400)
```
Failing is part of this game, whatever you do, at some point you will fail. The trick is not staying in that situation, let's see why this failure happened and turn it into success.
-First of all, using `assert` is not necessarily the best thing to do in your test. Foundry comes with better assertions than the basic solidity `assert` that doesn't tell you much. Access this [link](https://book.getfoundry.sh/reference/ds-test#asserting) to check examples of all the possible assertions.
-
-Replace the existing assertions with the following:
-
-```javascript
- assertEq(uint256(raffle.getRaffleState()),0);
- assertTrue(raffle.getRecentWinner() != address(0));
- assertEq(raffle.getNumberOfPlayers(), 0);
- assertGt(raffle.getLastTimeStamp(), previousTimeStamp);
- assertEq(raffle.getRecentWinner().balance, STARTING_USER_BALANCE + prize);
+Can you figure out what is wrong?
+If we trace the `InvalidRequest()` error, we see that it is emitted from the
+`_chargePayment` function in the `VRFCoordinatorV2_5Mock` contract. It looks
+like we have not funded our subscription with enough LINK tokens. Let's fund it
+with more by updating our `Interactions.s.sol` where for now, I'll times our
+`FUND_AMOUNT` by 100.
+
+```solidity
+function fundSubscription(address vrfCoordinator, uint256 subscriptionId, address linkToken, address account) public {
+ console.log("Funding subscription:\t", subscriptionId);
+ console.log("Using vrfCoordinator:\t\t\t", vrfCoordinator);
+ console.log("On chainId: ", block.chainid);
+
+ if(block.chainid == ETH_ANVIL_CHAIN_ID) {
+ vm.startBroadcast(account);
+ VRFCoordinatorV2_5Mock(vrfCoordinator).fundSubscription(subscriptionId, FUND_AMOUNT * 100);
+ vm.stopBroadcast();
+ } else {
+ console.log(LinkToken(linkToken).balanceOf(msg.sender));
+ console.log(msg.sender);
+ console.log(LinkToken(linkToken).balanceOf(address(this)));
+ console.log(address(this));
+ vm.startBroadcast(account);
+ LinkToken(linkToken).transferAndCall(vrfCoordinator, FUND_AMOUNT, abi.encode(subscriptionId));
+ vm.stopBroadcast();
+ }
+}
```
Let's run the test again using `forge test --mt testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney -vvv`.
-Let's look at the output of the failing test:
-
-```
- ├─ [373] Raffle::getRaffleState() [staticcall]
- │ └─ ← [Return] 0
- ├─ [0] VM::assertEq(0, 0) [staticcall]
- │ └─ ← [Return]
- ├─ [431] Raffle::getRecentWinner() [staticcall]
- │ └─ ← [Return] 0x0000000000000000000000000000000000000005
- ├─ [0] VM::assertTrue(true) [staticcall]
- │ └─ ← [Return]
- ├─ [369] Raffle::getNumberOfPlayers() [staticcall]
- │ └─ ← [Return] 0
- ├─ [0] VM::assertEq(0, 0) [staticcall]
- │ └─ ← [Return]
- ├─ [325] Raffle::getLastTimeStamp() [staticcall]
- │ └─ ← [Return] 32
- ├─ [0] VM::assertGt(32, 1) [staticcall]
- │ └─ ← [Return]
- ├─ [431] Raffle::getRecentWinner() [staticcall]
- │ └─ ← [Return] 0x0000000000000000000000000000000000000005
- ├─ [0] VM::assertEq(10050000000000000000 [1.005e19], 10060000000000000000 [1.006e19]) [staticcall]
- │ └─ ← [Revert] assertion failed: 10050000000000000000 != 10060000000000000000
- └─ ← [Revert] assertion failed: 10050000000000000000 != 10060000000000000000
-
-Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 3.93ms (284.70µs CPU time)
-```
-
-Starting from the beginning, we see the `getRaffleState` call, then the `assertEq(0, 0)` which passes, this was our first check. After that we see `getRecentWinner` and the `assertTrue` that passes, this was the second check. Following that comes the `getNumberOfPlayers` call and the `assertEq(0, 0)` that passes, this was the third check. The next is `getLastTimeStamp` which returns `32`, followed by `assertGt(32, 1)` which yields true, because 32 is greater than 1, this was the fourth check. The only check left is the balance of the winner being equal to `STARTING_USER_BALANCE + prize`.
-
-We know from that execution tree that the winner is `address(5)` (0x0000000000000000000000000000000000000005). Let's go through all the things that changed the balance of `address(5)`:
-
-1. `address(5)` was hoaxed with STARTING_USER_BALANCE;
-2. `address(5)` paid the `entranceFee`;
-3. `address(5)` won the `prize`, i.e. `entranceFee * (additionalEntrants + 1)`.
-
-Did you spot it? If not please read the 3 balance changing actions and check it against our assertion.
-
-The 3 steps above are not represented in this check `assertEq(raffle.getRecentWinner().balance, STARTING_USER_BALANCE + prize);` because we never subtract the paid `entranceFee`.
-
-Change the last assertion to:
-
-```javascript
- assertEq(raffle.getRecentWinner().balance, STARTING_USER_BALANCE - entranceFee + prize);
-```
-
-Run the test with `forge test --mt testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney`.
+Run the test with `forge test --mt testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney`.
```
[⠢] Compiling...
No files changed, compilation skipped
Ran 1 test for test/unit/RaffleTest.t.sol:RaffleTest
-[PASS] testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney() (gas: 287531)
+[PASS] testFulfillRandomWordsPicksAWinnerResetsAndSendsMoney() (gas: 287531)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.93ms (250.30µs CPU time)
```
Amazing work! Let's try some forked tests in the next lesson.
-
diff --git a/courses/foundry/4-smart-contract-lottery/38-forked-test-environment-and-dynamic-private-keys/+page.md b/courses/foundry/4-smart-contract-lottery/38-forked-test-environment-and-dynamic-private-keys/+page.md
index 872e3a971..fe1c26104 100644
--- a/courses/foundry/4-smart-contract-lottery/38-forked-test-environment-and-dynamic-private-keys/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/38-forked-test-environment-and-dynamic-private-keys/+page.md
@@ -14,7 +14,7 @@ That being said:
1. Please rename your current `Raffle.sol` into `Raffle_Old.sol`. Create a new file called `Raffle.sol` and paste the following contract inside:
-```javascript
+```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
@@ -190,7 +190,7 @@ You will see that the new contract is very similar. It only has some changes rel
5. Copy the following inside the newly created file:
-```javascript
+```solidity
// SPDX-License-Identifier: MIT
// A mock for testing code that relies on VRFCoordinatorV2Plus.
pragma solidity 0.8.23;
@@ -641,29 +641,27 @@ Let's take these problems one by one. We tried to add a consumer, but it failed
Open your `HelperConfig.s.sol` and perform the following changes:
-```javascript
- function getSepoliaEthConfig()
- public
- view
- returns (NetworkConfig memory)
- {
- return NetworkConfig({
- entranceFee: 0.01 ether,
- interval: 30, // 30 seconds
- vrfCoordinator: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
- gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
- subscriptionId: 5000032745829988966686682423284879867102409618787289144283231874950241281744, // Your own subscriptionId goes here
- callbackGasLimit: 500000, // 500,000 gas
- link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,
- deployerKey: vm.envUint("SEPOLIA_PRIVATE_KEY")
- });
- }
+```solidity
+function getSepoliaEthConfig()
+ public
+ view
+ returns (NetworkConfig memory)
+{
+ return NetworkConfig({
+ entranceFee: 0.01 ether,
+ interval: 30, // 30 seconds
+ vrfCoordinator: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
+ gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
+ subscriptionId: 5000032745829988966686682423284879867102409618787289144283231874950241281744, // Your own subscriptionId goes here
+ callbackGasLimit: 500000, // 500,000 gas
+ link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,
+ deployerKey: vm.envUint("SEPOLIA_PRIVATE_KEY")
+ });
+}
```
-```javascript
+```solidity
contract HelperConfig is Script {
-
-
struct NetworkConfig {
uint256 entranceFee;
uint256 interval;
@@ -674,6 +672,7 @@ contract HelperConfig is Script {
address link;
uint256 deployerKey;
}
+}
```
Given that we've updated the struct above ... we are going to have to update a lot of places. Let's keep going inside `HelperConfig`.
@@ -692,39 +691,39 @@ Private Keys
Below the `struct NetworkConfig` create a new variable:
-```javascript
+```solidity
uint256 public constant DEFAULT_ANVIL_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
```
and use it inside the `getOrCreateAnvilEthConfig` function
-```javascript
- function getOrCreateAnvilEthConfig()
- public
- returns (NetworkConfig memory anvilNetworkConfig)
- {
- [...]
- LinkToken link = new LinkToken();
- vm.stopBroadcast();
-
- return NetworkConfig({
- entranceFee: 0.01 ether,
- interval: 30, // 30 seconds
- vrfCoordinator: address(vrfCoordinatorV2Mock),
- gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
- subscriptionId: 0, // If left as 0, our scripts will create one!
- callbackGasLimit: 500000, // 500,000 gas
- link: address(link),
- deployerKey: DEFAULT_ANVIL_KEY
- });
- }
+```solidity
+function getOrCreateAnvilEthConfig()
+ public
+ returns (NetworkConfig memory anvilNetworkConfig)
+{
+ [...]
+ LinkToken link = new LinkToken();
+ vm.stopBroadcast();
+
+ return NetworkConfig({
+ entranceFee: 0.01 ether,
+ interval: 30, // 30 seconds
+ vrfCoordinator: address(vrfCoordinatorV2Mock),
+ gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
+ subscriptionId: 0, // If left as 0, our scripts will create one!
+ callbackGasLimit: 500000, // 500,000 gas
+ link: address(link),
+ deployerKey: DEFAULT_ANVIL_KEY
+ });
+}
```
With this change, we have finished the work in `HelperConfig.s.sol`. Let's keep going with fixing `Interactions.s.sol`:
1. `AddConsumer` contract
-```javascript
+```solidity
contract AddConsumer is Script {
function addConsumer(address raffle, address vrfCoordinator, uint256 subscriptionId, uint256 deployerKey) public {
@@ -766,7 +765,7 @@ We start everything from `helperConfig.activeNetworkConfig` because the main cha
We will perform the same changes to the other two contracts:
-```javascript
+```solidity
contract FundSubscription is Script {
uint96 public constant FUND_AMOUNT = 3 ether;
@@ -808,7 +807,7 @@ contract FundSubscription is Script {
We start with `fundSubscriptionUsingConfig` we add the `deployerKey` variable in the `activeNetworkConfig` call line. We use that newly acquired `deployerKey` inside the `fundSubscription` call, providing it as input. Going to `fundSubscription` we add the 4th input variable `uint256 deployerKey`. We use the new input in both `vm.startBroadcast` places.
-```javascript
+```solidity
contract CreateSubscription is Script {
function createSubscriptionUsingConfig() public returns (uint256) {
@@ -887,7 +886,7 @@ Let's read it. We modified the 3 functions `createSubscription`, `fundSubscripti
Open the `DeployRaffle.s.sol` file and modify the 3 functions call to include `deployerKey` as an input parameter.
-```javascript
+```solidity
contract DeployRaffle is Script {
function run() external returns (Raffle, HelperConfig) {
HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!
@@ -933,7 +932,7 @@ contract DeployRaffle is Script {
Open the `RaffleTest.t.sol` and add the `deployerKey` in the variables section and in the `setUp` function where `helperConfig.activeNetworkConfig()` is called:
-```javascript
+```solidity
contract RaffleTest is Test {
event EnteredRaffle(address indexed player);
@@ -971,6 +970,7 @@ contract RaffleTest is Test {
) = helperConfig.activeNetworkConfig();
}
+}
```
Let's run `forge build` again. There are two possible outcomes here:
@@ -987,7 +987,7 @@ CompilerError: Stack too deep. Try compiling with `--via-ir` (cli) or the equiva
If you get this error just go and comment out the `deployerKey` lines in `Raffle.t.sol`:
-```javascript
+```solidity
contract RaffleTest is Test {
event EnteredRaffle(address indexed player);
@@ -1025,6 +1025,7 @@ contract RaffleTest is Test {
) = helperConfig.activeNetworkConfig();
}
+}
```
Run `forge build` again and everything should compile.
@@ -1056,40 +1057,40 @@ We should not run this test on Sepolia.
Add the following modifier in `Raffle.t.sol`:
-```javascript
- modifier skipFork() {
- if (block.chainid != 31337){
- return;
- }
- _;
+```solidity
+modifier skipFork() {
+ if (block.chainid != 31337){
+ return;
}
+ _;
+}
```
This will check the `block.chainid` to see if we are on Sepolia. If we are on Sepolia then it returns, skipping the test.
Add it next to the `raffleEnteredAndTimePassed` modifier:
-```javascript
- function testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep(uint256 requestId)
- public
- raffleEntredAndTimePassed
- skipFork
- {
- // Arrange
- // Act / Assert
- vm.expectRevert("nonexistent request");
- // vm.mockCall could be used here...
- VRFCoordinatorV2PlusMock(vrfCoordinator).fulfillRandomWords(
- requestId,
- address(raffle)
- );
- }
+```solidity
+function testFulfillRandomWordsCanOnlyBeCalledAfterPerformUpkeep(uint256 requestId)
+ public
+ raffleEntredAndTimePassed
+ skipFork
+{
+ // Arrange
+ // Act / Assert
+ vm.expectRevert("nonexistent request");
+ // vm.mockCall could be used here...
+ VRFCoordinatorV2PlusMock(vrfCoordinator).fulfillRandomWords(
+ requestId,
+ address(raffle)
+ );
+}
```
The other failing test is `testFulfillRandomWordsPicksAWinnerRestesAndSendsMoney`. Looking through its code we can see why it fails:
-```javascript
-// pretend to be Chainlink VRF
+```solidity
+// Pretend to be Chainlink VRF
VRFCoordinatorV2PlusMock(vrfCoordinator).fulfillRandomWords(
uint256(requestId),
address(raffle)
diff --git a/courses/foundry/4-smart-contract-lottery/39-creating-integration-tests/+page.md b/courses/foundry/4-smart-contract-lottery/39-creating-integration-tests/+page.md
index ff68ee635..b0f4ec7cd 100644
--- a/courses/foundry/4-smart-contract-lottery/39-creating-integration-tests/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/39-creating-integration-tests/+page.md
@@ -11,17 +11,17 @@ We have finished the unit tests. Amazing!
But that doesn't mean we are finished with testing. Unit tests are just the first step. If you remember what we learned some lessons ago there are 4 types of tests:
-1. Unit tests - Basic tests that check the functionality;
-2. Integration tests - We test our deployment scripts and other components of our contracts;
-3. Forked tests - Pseudo staging;
-4. Staging tests - We run tests on a mainnet/testnet;
+1. Unit tests - Basic tests that check the functionality
+2. Integration tests - We test our deployment scripts and other components of our contracts
+3. Forked tests - Pseudo staging
+4. Staging tests - We run tests on a mainnet/testnet
We won't cover staging tests for now, but these are very important. People often deploy their entire protocols on testnets or cheap mainnets like Polygon to properly check how their protocols behave in production environments.
-Testnets are not always fun to interact with (just remember the gigantic refactoring we had to do in the previous lesson), but they are the closest thing to the mainnet we have. To check the recommended testnet please access the [Foundry Full Course F23 repo](https://github.com/Cyfrin/foundry-full-course-f23).
+Testnets are not always fun to interact with (just remember the gigantic refactoring we had to do in the previous lesson), but they are the closest thing to the mainnet we have. To check the recommended testnet please access the [Foundry Full Course repo](https://github.com/Cyfrin/foundry-full-course-cu).
Back to integration testing, we already started testing the deployment script given that in the `RaffleTest.t.sol::setUp` function we used the script to deploy our Raffle contract. A better approach would have been to create an `InteractionsTest.t.sol` and test the deploy scripts first, then use them in the `RaffleTest.t.sol`.
We will not do staging tests because writing scripts that do a lot of waiting is a bit tricky. Foundry is phenomenal in testing things that are 100% on chain, but given that we are using Chainlink VRF and some things that happen off-chain, we will struggle to do these staging tests.
-If you want to try your hand in writing some tests go ahead and write the `InteractionsTest.t.sol` tests. These should cover at least the `DeployRaffle.s.sol`. If you want to score extra points and be a true champion, then cover all the scripts.
\ No newline at end of file
+If you want to try your hand in writing some tests go ahead and write the `InteractionsTest.t.sol` tests. These should cover at least the `DeployRaffle.s.sol`. If you want to score extra points and be a true champion, then cover all the scripts.
diff --git a/courses/foundry/4-smart-contract-lottery/4-creating-custom-errors/+page.md b/courses/foundry/4-smart-contract-lottery/4-creating-custom-errors/+page.md
index 362716f01..15e5880ae 100644
--- a/courses/foundry/4-smart-contract-lottery/4-creating-custom-errors/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/4-creating-custom-errors/+page.md
@@ -11,16 +11,15 @@ Great! Let's move on with writing the contract.
Previously we defined the `i_entranceFee` variable. This is the amount the user has to send to enter the raffle. How do we check this?
-```javascript
- function enterRaffle() external payable {
- require(msg.value >= i_entranceFee, "Not enough ETH sent");
- }
-
+```solidity
+function enterRaffle() external payable {
+ require(msg.value >= i_entranceFee, "Not enough ETH sent");
+}
```
-First, we changed the visibility from `public` to `external`. `External` is more gas efficient, and we won't call the `enterRaffle` function internally.
+First, we changed the visibility from `public` to `external`. `external` is more gas efficient, and we won't call the `enterRaffle` function internally.
-We used a `require` statement to ensure that the `msg.value` is higher than `i_entranceFee`. If that is false we will yield an error message `"Not enough ETH sent"`.
+We used a `require` statement to ensure that the `msg.value` is higher than `i_entranceFee`. If that is false, we will yield an error message `"Not enough ETH sent"`.
**Note: The `require` statement is used to enforce certain conditions at runtime. If the condition specified in the `require` statement evaluates to `false`, the transaction is reverted, and any changes made to the state within that transaction are undone. This is useful for ensuring that certain prerequisites or validations are met before executing further logic in a smart contract.**
@@ -34,16 +33,16 @@ I know we just wrote this using the `require` statement, we did that because `re
We will refactor `enterRaffle`, but before that let's define our custom error. Be mindful of the layout we talked about in the previous lesson
-```javascript
+```solidity
error Raffle_NotEnoughEthSent();
```
Now the `enterRaffle()` function:
-```javascript
- function enterRaffle() external payable {
- // require(msg.value >= i_entranceFee, "Not enough ETH sent!");
- if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
- }
+```solidity
+function enterRaffle() external payable {
+ // require(msg.value >= i_entranceFee, "Not enough ETH sent!");
+ if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
+}
```
You will see that we named the custom error using the `Raffle__` prefix. This is a very good practice that will save you a ton of time when you need to debug a protocol with 20 smart contracts. You will run your tests and then ask yourself `Ok, it failed with this error ... but where does this come from?`. Because you thought ahead and used prefixes in naming your error you won't have that problem! Awesome!
@@ -54,11 +53,11 @@ You will see that we named the custom error using the `Raffle__` prefix. This is
There is no difference between this:
-```javascript
+```solidity
if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
```
and this:
-```javascript
+```solidity
if(msg.value < i_entranceFee) {
revert Raffle__NotEnoughEthSent();
}
diff --git a/courses/foundry/4-smart-contract-lottery/41-implementing-console-log-in-your-smart-contract/+page.md b/courses/foundry/4-smart-contract-lottery/41-implementing-console-log-in-your-smart-contract/+page.md
index 2e6785402..d493366bb 100644
--- a/courses/foundry/4-smart-contract-lottery/41-implementing-console-log-in-your-smart-contract/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/41-implementing-console-log-in-your-smart-contract/+page.md
@@ -11,16 +11,16 @@ Do you remember how inside our `Raffle.t.sol` we did `import {Test, console} fro
1. Add the following in the import section of your `Raffle.sol` file: `import {console} from "forge-std/Script.sol";`
2. Change the `enterRaffle` function as follows:
-
-```javascript
- function enterRaffle() public payable {
- if (s_raffleState == RaffleState.CALCULATING) revert Raffle__RaffleNotOpen();
- if (msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
- console.log("Debugging at its finest");
- s_players.push(payable(msg.sender));
-
- emit EnteredRaffle(msg.sender);
- }
+
+```solidity
+function enterRaffle() public payable {
+ if (s_raffleState == RaffleState.CALCULATING) revert Raffle__RaffleNotOpen();
+ if (msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
+ console.log("Debugging at its finest");
+ s_players.push(payable(msg.sender));
+
+ emit EnteredRaffle(msg.sender);
+}
```
3. Run `forge test --mt testRaffleRecordsPlayerWhenTheyEnter -vv`.
@@ -47,4 +47,4 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 11.59ms (84.60µs C
You can see the `Debugging at its finest` message at the end of the log. Super nice!
-**Note:** Make sure to delete those before deploying to mainnet, because this will cost gas, and you do not want to spend that!
\ No newline at end of file
+**Note:** Make sure to delete those before deploying to mainnet, because this will cost gas, and you do not want to spend that!
diff --git a/courses/foundry/4-smart-contract-lottery/43-recap/+page.md b/courses/foundry/4-smart-contract-lottery/43-recap/+page.md
index c38faaa1a..4451d7e68 100644
--- a/courses/foundry/4-smart-contract-lottery/43-recap/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/43-recap/+page.md
@@ -34,7 +34,7 @@ The automation upkeep of our smart contracts led to an amazing result—it ran o
## Smart Contract Execution and Testing
-Once triggered, the Chainlink network replies by calling the `fulfill random words` function, which selects our random winner. We got a good look into the CEI - checks effects interactions pattern, where we implement checks, conduct effects and eventually process our external interactions outside of the smart contracts.
+Once triggered, the Chainlink network replies by calling the `fulfillRandomWords` function, which selects our random winner. We got a good look into the CEI - checks effects interactions pattern, where we implement checks, conduct effects and eventually process our external interactions outside of the smart contracts.
We provided several getter functions. Surprisingly, the codebase for this project is only about 200 lines long, but it felt much longer because of the advanced scripting and deployment methods we had to learn.
@@ -52,7 +52,7 @@ During the process, we wrote comprehensive unit tests, though we intentionally l
We also worked a lot with modifiers and expected a revert with this `abi encoder` thing. Understanding that will be a task for later.
-Finally, we deployed this lottery on an actual testnet chain, funding our automation subscription and our VRF subscription with Link. We observed chainlink nodes handling all this with no issues.
+Finally, we deployed this lottery on an actual testnet chain, funding our automation subscription and our VRF subscription with Link. We observed Chainlink nodes handling all this with no issues.
## Recap
diff --git a/courses/foundry/4-smart-contract-lottery/5-smart-contracts-events/+page.md b/courses/foundry/4-smart-contract-lottery/5-smart-contracts-events/+page.md
index e36405552..ccf245629 100644
--- a/courses/foundry/4-smart-contract-lottery/5-smart-contracts-events/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/5-smart-contracts-events/+page.md
@@ -11,9 +11,9 @@ Ok, our user paid the entrance fee, but how do we track his registration? We can
Take a moment and decide what would be the best from the following:
-1. Mapping;
-2. Array;
-3. A bunch of address variables and limit the number of participants;
+1. Mapping
+2. Array
+3. A bunch of address variables and limit the number of participants
.
.
@@ -27,11 +27,11 @@ We've made it `address payable` because one of the participants registered in th
Back in the `enterRaffle` function, we need to add the address that paid into the `s_players` array:
-```javascript
- function enterRaffle() external payable {
- if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
- s_players.push(payable(msg.sender));
- }
+```solidity
+function enterRaffle() external payable {
+ if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
+ s_players.push(payable(msg.sender));
+}
```
The `.push` method is used to append an element to an array, increasing its length by 1.
@@ -48,7 +48,7 @@ How can we use events?
Imagine we have a more complex function that changes an important parameter, let's say we are recording the exchange rate of BTC/USDC. We change it by calling the function `changeER()`. After we perform the call and the exchange rate is changed we need to make sure this also gets picked up by our front-end. We make the front-end listen for the `BTCUSDCupdated` event. An example of that event could be this:
-```javascript
+```solidity
event BTCUSDCupdated(
uint256 indexed oldER,
uint256 indexed newER,
@@ -64,26 +64,20 @@ For an event to be logged we need to emit it.
Let's come back to our `Raffle` contract where we'll also learn how to emit them.
First, we define the event (be mindful of where the events should go in terms of our defined layout)
-```javascript
+```solidity
event EnteredRaffle(address indexed player);
```
Then, we emit it in `enterRaffle`:
-```javascript
- function enterRaffle() external payable {
- if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
- s_players.push(payable(msg.sender));
- emit EnteredRaffle(msg.sender);
- }
+```solidity
+function enterRaffle() external payable {
+ if(msg.value < i_entranceFee) revert Raffle__NotEnoughEthSent();
+ s_players.push(payable(msg.sender));
+ emit EnteredRaffle(msg.sender);
+}
```
Great! I know there is a possibility you don't quite understand the importance/usage of this event, but don't worry, we'll get back to it in the testing section.
But before that, let's discuss randomness.
-
-
-
-
-
-
diff --git a/courses/foundry/4-smart-contract-lottery/6-random-numbers-block-timestamp/+page.md b/courses/foundry/4-smart-contract-lottery/6-random-numbers-block-timestamp/+page.md
index 96b062e27..1c257eb2e 100644
--- a/courses/foundry/4-smart-contract-lottery/6-random-numbers-block-timestamp/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/6-random-numbers-block-timestamp/+page.md
@@ -11,14 +11,14 @@ Going back to [lesson 1](https://updraft.cyfrin.io/courses/foundry/smart-contrac
What do we need to do that?
-1. A random number;
-2. Use the random number to pick a winning player;
-3. Call `pickWinner` automatically;
+1. A random number
+2. Use the random number to pick a winning player
+3. Call `pickWinner` automatically
For now, let's focus on points 1 and 2. But before diving straight into the randomness let's think a bit about the Raffle design. We don't have any problem with anyone calling `pickWinner`. As long as someone wants to pay the gas associated with that they are more than welcome to do it. But we need to make sure that a decent amount of time passed since the start of the raffle. We don't want to host a 10-second raffle where two people get to register and then someone calls the `pickWinner`. In that sense, we need to define a new state variable called `i_interval` which represents the duration of a raffle:
-```javascript
-contract Raffle{
+```solidity
+contract Raffle {
error Raffle__NotEnoughEthSent();
@@ -33,13 +33,14 @@ contract Raffle{
i_entranceFee = entranceFee;
i_interval = interval;
}
+}
```
Now that we have defined a raffle duration, we need to check it in `pickWinner`, but check it against what? We need to check it against the difference between the moment in time when the raffle started and the moment in time when the function `pickWinner` is called. But for that, we need to record the raffle starting time.
Perform the following update:
-```javascript
+```solidity
contract Raffle{
error Raffle__NotEnoughEthSent();
@@ -57,24 +58,19 @@ contract Raffle{
i_interval = interval;
s_lastTimeStamp = block.timestmap;
}
+}
```
And now we have all the prerequisites to perform the check:
-```javascript
- // 1. Get a random number
- // 2. Use the random number to pick a player
- // 3. Automatically called
- function pickWinner() external {
- // check to see if enough time has passed
- if (block.timestamp - s_lastTimeStamp < interval) revert();
- }
+```solidity
+// 1. Get a random number
+// 2. Use the random number to pick a player
+// 3. Automatically called
+function pickWinner() external {
+ // check to see if enough time has passed
+ if (block.timestamp - s_lastTimeStamp < interval) revert();
+}
```
Don't worry! We will create a custom error for that in the next lesson. But before that let's talk randomness.
-
-
-
-
-
-
diff --git a/courses/foundry/4-smart-contract-lottery/7-random-numbers-introduction-to-chainlink-vrf/+page.md b/courses/foundry/4-smart-contract-lottery/7-random-numbers-introduction-to-chainlink-vrf/+page.md
index 26e9eb790..c78aef625 100644
--- a/courses/foundry/4-smart-contract-lottery/7-random-numbers-introduction-to-chainlink-vrf/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/7-random-numbers-introduction-to-chainlink-vrf/+page.md
@@ -27,7 +27,7 @@ Go [here](https://docs.chain.link/vrf/v2/subscription/examples/get-a-random-numb
Press on the blue button that says `Create Subscription`. You don't need to provide a project name or an email address, but you can if you want to.
-When you press `Create subscription` you will need to approve the subscription creation. Sign it using your Metamask and wait until you receive the confirmation. You will be asked to sign the message again. If you are not taken to the `Add Funds` page, go to `My Subscriptions` section and click on the id of the subscription you just created, then click on `Actions` and `Fund subscription`. Proceed in funding your subscription.
+When you press `Create subscription` you will need to approve the subscription creation. Sign it using your MetaMask and wait until you receive the confirmation. You will be asked to sign the message again. If you are not taken to the `Add Funds` page, go to `My Subscriptions` section and click on the id of the subscription you just created, then click on `Actions` and `Fund subscription`. Proceed in funding your subscription.
The next step is adding consumers. On the same page, we clicked on the `Actions` button you can find a button called `Add consumer`. You will be prompted with an `Important` message that communicates your `Subscription ID`. That is a very important thing that we'll use in our smart contract.
@@ -49,17 +49,16 @@ Build and deploy the contract on Sepolia.
Ignoring the configuration parameters for now let's look through the most important elements of the contract:
-```javascript
- struct RequestStatus {
- bool fulfilled; // whether the request has been successfully fulfilled
- bool exists; // whether a requestId exists
- uint256[] randomWords;
- }
- mapping(uint256 => RequestStatus)
- public s_requests; /* requestId --> requestStatus */
-
- uint256[] public requestIds;
- uint256 public lastRequestId;
+```solidity
+struct RequestStatus {
+ bool fulfilled; // whether the request has been successfully fulfilled
+ bool exists; // whether a requestId exists
+ uint256[] randomWords;
+}
+
+mapping(uint256 => RequestStatus) public s_requests; // requestId --> requestStatus
+uint256[] public requestIds;
+uint256 public lastRequestId;
```
This is the way the contract keeps track of the requests, their status and the `randomWords` provided as a response to the requests. The mapping uses the `requestId` as a key and the details regarding the request are stored inside the `RequestStatus` struct which acts as a mapping value. Given that we can't loop through mappings we will also have a `requestIds` array. We also record the `lastRequestId` for efficiency.
@@ -68,38 +67,38 @@ We will also store the `subscriptionId` as a state variable, this will be checke
The next important piece is the `VRFCoordinatorV2Interface` which is one of the dependencies we import, this [contract](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol) has a lot of methods related to subscription management and requests, but the one we are interested in right now is `requestRandomWords`, this is the function that we need to call to trigger the process of receiving the random words, that we'll use as a source of randomness in our application.
-```javascript
+```solidity
// Assumes the subscription is funded sufficiently.
- function requestRandomWords()
- external
- onlyOwner
- returns (uint256 requestId)
- {
- // Will revert if subscription is not set and funded.
- requestId = COORDINATOR.requestRandomWords(
- keyHash,
- s_subscriptionId,
- requestConfirmations,
- callbackGasLimit,
- numWords
- );
- s_requests[requestId] = RequestStatus({
- randomWords: new uint256[](0),
- exists: true,
- fulfilled: false
- });
- requestIds.push(requestId);
- lastRequestId = requestId;
- emit RequestSent(requestId, numWords);
- return requestId;
- }
+function requestRandomWords()
+ external
+ onlyOwner
+ returns (uint256 requestId)
+{
+ // Will revert if subscription is not set and funded.
+ requestId = COORDINATOR.requestRandomWords(
+ keyHash,
+ s_subscriptionId,
+ requestConfirmations,
+ callbackGasLimit,
+ numWords
+ );
+ s_requests[requestId] = RequestStatus({
+ randomWords: new uint256[](0),
+ exists: true,
+ fulfilled: false
+ });
+ requestIds.push(requestId);
+ lastRequestId = requestId;
+ emit RequestSent(requestId, numWords);
+ return requestId;
+}
```
This function is the place where we call the `requestRandomWords` on the `VRFCoordinatorV2Interface` which sends us back the `requestId`. We record this `requestId` in the mapping, creating its `RequestStatus`, we push it into the `requestIds` array and update the `lastRequestId` variable. The function returns the `requestId`.
After calling the function above, Chainlink will call your `fulfillRandomWords` function. They will provide the `_requestId` corresponding to your `requestRandomWords` call together with the `_randomWrods`. It updates the `fulfilled` and `randomWords` struct parameters. In real-world applications, this is where the logic happens. If you have to assign some traits to an NFT, roll a dice, draw the raffle winner, etc.
-Great! Let's come back to the configuration parameters. The `keyHash` variable represents the gas lane we want to use. Think of those as the maximum gas price you are willing to pay for a request in WEI. It functions as an ID of the off-chain VRF job that runs in response to requests.
+Great! Let's come back to the configuration parameters. The `keyHash` variable represents the gas lane we want to use. Think of those as the maximum gas price you are willing to pay for a request in gwei. It functions as an ID of the off-chain VRF job that runs in response to requests.
```
200 gwei Key Hash 0x8af398995b04c28e9951adb9721ef74c74f93e6a478f39e7e0777be13527e7ef
@@ -115,4 +114,4 @@ The same page contains information about `Max Gas Limit` and `Minimum Confirmati
Another extremely important aspect related to Chainlink VRF is understanding its `Security Considerations`. Please read them [here](https://docs.chain.link/vrf/v2-5/security#use-requestid-to-match-randomness-requests-with-their-fulfillment-in-order).
-I know this lesson was a bit abstract. But let's implement this in our project in the next lesson. See you there!
\ No newline at end of file
+I know this lesson was a bit abstract. But let's implement this in our project in the next lesson. See you there!
diff --git a/courses/foundry/4-smart-contract-lottery/8-implement-the-chainlink-vrf/+page.md b/courses/foundry/4-smart-contract-lottery/8-implement-the-chainlink-vrf/+page.md
index b0af4b15a..e43df5b7e 100644
--- a/courses/foundry/4-smart-contract-lottery/8-implement-the-chainlink-vrf/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/8-implement-the-chainlink-vrf/+page.md
@@ -7,35 +7,41 @@ _Follow along with this video:_
### Getting Started with Chainlink VRF
+
+> 🗒️ **NOTE**:
+> This written lesson uses VRF V2. Video lesson uses VRF V2.5. There are
+some changes. Import path for VRF contract is slightly different, and the
+`requestRandomWords()` parameter is slightly different**
+
Continuing the previous lesson, let's integrate Chainlink VRF into our Raffle project.
Coming back to our `pickWinner` function.
-```javascript
- // 1. Get a random number
- // 2. Use the random number to pick a player
- // 3. Automatically called
- function pickWinner() external {
- // check to see if enough time has passed
- if (block.timestamp - s_lastTimeStamp < i_interval) revert();
- }
+```solidity
+// 1. Get a random number
+// 2. Use the random number to pick a player
+// 3. Automatically called
+function pickWinner() external {
+ // check to see if enough time has passed
+ if (block.timestamp - s_lastTimeStamp < i_interval) revert();
+}
```
Let's focus on points 1 and 2. In the previous lesson, we learned that we need to request a `randomWord` and Chainlink needs to callback one of our functions to answer the request. Let's copy the `requestId` line from the [Chainlink VRF docs](https://docs.chain.link/vrf/v2/subscription/examples/get-a-random-number#analyzing-the-contract) example inside our `pickWinner` function and start fixing the missing dependencies.
-```javascript
- function pickWinner() external {
- // check to see if enough time has passed
- if (block.timestamp - s_lastTimeStamp < i_interval) revert();
+```solidity
+function pickWinner() external {
+ // check to see if enough time has passed
+ if (block.timestamp - s_lastTimeStamp < i_interval) revert();
- uint256 requestId = COORDINATOR.requestRandomWords(
- keyHash,
- s_subscriptionId,
- requestConfirmations,
- callbackGasLimit,
- numWords
- );
- }
+ uint256 requestId = COORDINATOR.requestRandomWords(
+ keyHash,
+ s_subscriptionId,
+ requestConfirmations,
+ callbackGasLimit,
+ numWords
+ );
+}
```
You know the `keyHash`, `subId`, `requestConfirmations`, `callbackGasLimit` and `numWords` from our previous lesson.
@@ -46,39 +52,39 @@ Ok, starting from the beginning what do we need?
2. We need to take care of the VRF Coordinator, define it as an immutable variable and give it a value in the constructor;
Let's add the following imports:
-```javascript
+```solidity
import {VRFCoordinatorV2Interface} from "chainlink/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol";
import {VRFConsumerBaseV2} from "chainlink/src/v0.8/vrf/VRFConsumerBaseV2.sol";
```
Let's make our contract inherit the `VRFConsumerBaseV2`:
-```javascript
+```solidity
contract Raffle is VRFConsumerBaseV2
```
Add a new immutable variable:
-```javascript
- // Chainlink VRF related variables
- address immutable i_vrfCoordinator;
+```solidity
+// Chainlink VRF related variables
+address immutable i_vrfCoordinator;
```
I've divided the `Raffle` variables from the `Chainlink VRF` variables to keep the contract tidy.
Adjust the constructor to accommodate all the new variables and imports:
-```javascript
- constructor(uint256 entranceFee, uint256 interval, address vrfCoordinator) {
- i_entranceFee = entranceFee;
- i_interval = interval;
- s_lastTimeStamp = block.timestamp;
+```solidity
+constructor(uint256 entranceFee, uint256 interval, addsress vrfCoordinator) {
+ i_entranceFee = entranceFee;
+ i_interval = interval;
+ s_lastTimeStamp = block.timestamp;
- i_vrfCoordinator = vrfCoordinator;
- }
+ i_vrfCoordinator = vrfCoordinator;
+}
```
For our imports to work we need to install the Chainlink library, and run the following command in your terminal:
-```
+```bash
forge install smartcontractkit/chainlink@42c74fcd30969bca26a9aadc07463d1c2f473b8c --no-commit
```
@@ -107,7 +113,7 @@ forge remappings>remappings.txt
```
This will create a new file that contains your project remappings:
-```
+```toml
chainlink/=lib/chainlink/contracts/
forge-std/=lib/forge-std/src/
```
@@ -165,14 +171,14 @@ At least now we know what's left :smile:
Let's add the above-mentioned variables inside the VRF state variables block:
-```javascript
+```solidity
// Chainlink VRF related variables
- VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
- bytes32 private immutable i_gasLane;
- uint64 private immutable i_subscriptionId;
- uint16 private constant REQUEST_CONFIRMATIONS = 3;
- uint32 private immutable i_callbackGasLimit;
- uint32 private constant NUM_WORDS = 1;
+VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
+bytes32 private immutable i_gasLane;
+uint64 private immutable i_subscriptionId;
+uint16 private constant REQUEST_CONFIRMATIONS = 3;
+uint32 private immutable i_callbackGasLimit;
+uint32 private constant NUM_WORDS = 1;
```
We have changed the `keyHash` name to `i_gasLane` which is more descriptive for its purpose. Also, we've changed the type of `i_vrfCoordinator`. For our `pickWinner` function to properly call `uint256 requestId = i_vrfCoordinator.requestRandomWords(` we need that `i_vrfCoordinator` to be a contract, specifically the `VRFCoordinatorV2Interface` contract that we've imported.
@@ -180,17 +186,17 @@ For simplicity we request only 1 word, thus we make that variable constant. The
The next step is to attribute values inside the constructor:
-```javascript
- constructor(uint256 entranceFee, uint256 interval, address vrfCoordinator, bytes32 gasLane, uint64 subscriptionId, uint32 callbackGasLimit) VRFConsumerBaseV2(vrfCoordinator) {
- i_entranceFee = entranceFee;
- i_interval = interval;
- s_lastTimeStamp = block.timestamp;
+```solidity
+constructor(uint256 entranceFee, uint256 interval, address vrfCoordinator, bytes32 gasLane, uint64 subscriptionId, uint32 callbackGasLimit) VRFConsumerBaseV2(vrfCoordinator) {
+ i_entranceFee = entranceFee;
+ i_interval = interval;
+ s_lastTimeStamp = block.timestamp;
- i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinator);
- i_gasLane = gasLane;
- i_subscriptionId = subscriptionId;
- i_callbackGasLimit = callbackGasLimit;
- }
+ i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinator);
+ i_gasLane = gasLane;
+ i_subscriptionId = subscriptionId;
+ i_callbackGasLimit = callbackGasLimit;
+}
```
Ok, breathe, it's a lot but it's not complicated, let's go through it together:
@@ -202,7 +208,7 @@ Ok, breathe, it's a lot but it's not complicated, let's go through it together:
The last step is to create a new function:
-```javascript
+```solidity
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {}
```
@@ -223,5 +229,5 @@ Warning (2072): Unused local variable.
```
-Perfect! Don't worry we will use that `requestId` in a future lesson.
+Perfect! Don't worry. We will use that `requestId` in a future lesson.
diff --git a/courses/foundry/4-smart-contract-lottery/9-implementing-vrf-fulfil/+page.md b/courses/foundry/4-smart-contract-lottery/9-implementing-vrf-fulfil/+page.md
index 0d180d143..1848000d4 100644
--- a/courses/foundry/4-smart-contract-lottery/9-implementing-vrf-fulfil/+page.md
+++ b/courses/foundry/4-smart-contract-lottery/9-implementing-vrf-fulfil/+page.md
@@ -8,7 +8,7 @@ _Follow along with the video_
To work with the Chainlink VRF (Verifiable Random Function) in Solidity, we need to inherit functions from an **abstract contract** called [`VRFConsumerBaseV2Plus`](https://github.com/smartcontractkit/chainlink-brownie-contracts/blob/12393bd475bd60c222ff12e75c0f68effe1bbaaf/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol). Abstract contracts can contain both defined and undefined functions, such as:
-```js
+```solidity
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;
```
@@ -21,7 +21,7 @@ function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) i
Here’s how you override the `fulfillRandomWords` function:
-```js
+```solidity
function fulfillRandomWords(uint256, /* requestId */ uint256[] calldata randomWords) internal override {
//pick a winner here, send him the reward and reset the raffle
}
diff --git a/courses/uniswap-v2/7-twap/1-twap-spot-price-oracle/+page.md b/courses/uniswap-v2/7-twap/1-twap-spot-price-oracle/+page.md
index f2a717d3a..ec7cbe218 100644
--- a/courses/uniswap-v2/7-twap/1-twap-spot-price-oracle/+page.md
+++ b/courses/uniswap-v2/7-twap/1-twap-spot-price-oracle/+page.md
@@ -16,9 +16,7 @@ Let's imagine a lending protocol that allows users to borrow DAI by locking ETH
A hacker could exploit this protocol by manipulating the spot price of ETH. They could buy a large amount of ETH with DAI, increasing the price. Then, they could borrow a large amount of DAI, exceeding the value of their collateral.
-**Diagram:** [Insert Diagram Here]
-
-In the diagram, the hacker:
+Imagine a hacker:
1. Buys 832 WETH using 10,000,000 DAI, increasing the spot price of ETH to 71,819 DAI per ETH.
2. Borrows 5,745,599 DAI by locking 100 ETH as collateral.