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 4a33359e..aac6c050 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 @@ -112,7 +112,6 @@ verify: ---- Now, by running `make anvil` (open a new terminal once your chain has started!) followed by `make deploy`... 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 775c2318..4fa8a343 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 @@ -243,7 +243,6 @@ Can you write the rest of the tests? Please include tests for: ---- > ❗ **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. @@ -360,7 +359,6 @@ contract OurTokenTest is StdCheats, Test { ---- 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. diff --git a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/15-svg-deploy/+page.md b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/15-svg-deploy/+page.md index 40452173..73122161 100644 --- a/courses/advanced-foundry/2-how-to-create-an-NFT-collection/15-svg-deploy/+page.md +++ b/courses/advanced-foundry/2-how-to-create-an-NFT-collection/15-svg-deploy/+page.md @@ -233,7 +233,6 @@ contract MoodNFTTest is Test { ---- With these adjustments, our tests should function identically to before. 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 87e6921a..606cdb23 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 @@ -89,7 +89,6 @@ We can see this in Etherscan for any contract we've deployed. Here's an [**examp ---- 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. 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 f0da8751..8a831fa4 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 @@ -75,7 +75,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. 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 780dd47b..0503ff80 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 @@ -250,7 +250,6 @@ 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`. @@ -291,7 +290,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` 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 d760296f..aadd3982 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 @@ -79,7 +79,6 @@ flipMoodNft: ---- Assuming our `.env` is ready to go, we should be able to run the following... diff --git a/courses/advanced-foundry/3-develop-defi-protocol/10-defi-deploy-script/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/10-defi-deploy-script/+page.md index e1241adc..ac1b11fd 100644 --- a/courses/advanced-foundry/3-develop-defi-protocol/10-defi-deploy-script/+page.md +++ b/courses/advanced-foundry/3-develop-defi-protocol/10-defi-deploy-script/+page.md @@ -193,7 +193,6 @@ contract MockV3Aggregator { ---- Once mocks are deployed, we can configure the anvilNetworkConfig with those deployed addresses, and return this struct. 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 6918a934..66b9fe41 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 @@ -237,7 +237,6 @@ contract DSCEngine is ReentrancyGuard { ---- Welcome back! I'm excited to keep going. So far our DSCEngine.sol has quite a bit of functionality already. We've the ability to mint DSC, we can deposit collateral, check account information and more. 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 6afe7d4e..29eab29b 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 @@ -244,7 +244,6 @@ contract DSCEngine is ReentrancyGuard { ---- ### redeemCollateral 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 adeb2a9a..a0e0655e 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 @@ -276,7 +276,6 @@ contract DSCEngine is ReentrancyGuard { ---- ### Liquidation 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 dcdcf6a7..6c0335ba 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 @@ -310,7 +310,6 @@ contract DSCEngine is ReentrancyGuard { ---- ### Liquidation/Refactoring diff --git a/courses/advanced-foundry/3-develop-defi-protocol/17-defi-handler-fuzz-tests/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/17-defi-handler-fuzz-tests/+page.md index 8ad98cc2..aa1cbcd4 100644 --- a/courses/advanced-foundry/3-develop-defi-protocol/17-defi-handler-fuzz-tests/+page.md +++ b/courses/advanced-foundry/3-develop-defi-protocol/17-defi-handler-fuzz-tests/+page.md @@ -112,7 +112,6 @@ function getHealthFactor(address user) external view returns (uint256) { ---- If you managed to improve your coverage, even if not to this extent, you should be proud of getting this far. This code base is hard to write tests for and a lot of it comes with experience, practice and familiarity. 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 38ad3642..ae8d0976 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 @@ -110,7 +110,6 @@ contract InvariantsTest is StdInvariant Test { ---- We can see this fails for the expected reasons below. diff --git a/courses/advanced-foundry/3-develop-defi-protocol/22-defi-fuzz-debugging/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/22-defi-fuzz-debugging/+page.md index e62a884b..f6d4366d 100644 --- a/courses/advanced-foundry/3-develop-defi-protocol/22-defi-fuzz-debugging/+page.md +++ b/courses/advanced-foundry/3-develop-defi-protocol/22-defi-fuzz-debugging/+page.md @@ -177,7 +177,6 @@ Once more with feeling. ---- ### Wrap Up 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 c4f5657d..4c867dfc 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 @@ -509,4 +509,3 @@ contract DSCEngine is ReentrancyGuard { ---- diff --git a/courses/advanced-foundry/3-develop-defi-protocol/7-defi-mint-dsc/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/7-defi-mint-dsc/+page.md index cad01b17..bd5de429 100644 --- a/courses/advanced-foundry/3-develop-defi-protocol/7-defi-mint-dsc/+page.md +++ b/courses/advanced-foundry/3-develop-defi-protocol/7-defi-mint-dsc/+page.md @@ -491,4 +491,3 @@ contract DSCEngine is ReentrancyGuard { ---- diff --git a/courses/advanced-foundry/3-develop-defi-protocol/8-defi-health-factor/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/8-defi-health-factor/+page.md index 9374c9c5..41542068 100644 --- a/courses/advanced-foundry/3-develop-defi-protocol/8-defi-health-factor/+page.md +++ b/courses/advanced-foundry/3-develop-defi-protocol/8-defi-health-factor/+page.md @@ -358,4 +358,3 @@ contract DSCEngine is ReentrancyGuard { ---- diff --git a/courses/advanced-foundry/3-develop-defi-protocol/9-defi-finishing-mint-dsc/+page.md b/courses/advanced-foundry/3-develop-defi-protocol/9-defi-finishing-mint-dsc/+page.md index 8412945a..a634570e 100644 --- a/courses/advanced-foundry/3-develop-defi-protocol/9-defi-finishing-mint-dsc/+page.md +++ b/courses/advanced-foundry/3-develop-defi-protocol/9-defi-finishing-mint-dsc/+page.md @@ -291,4 +291,3 @@ contract DSCEngine is ReentrancyGuard { ---- diff --git a/courses/advanced-foundry/6-account-abstraction/11-signed-user-op/+page.md b/courses/advanced-foundry/6-account-abstraction/11-signed-user-op/+page.md index 7a738b4e..ee067e69 100644 --- a/courses/advanced-foundry/6-account-abstraction/11-signed-user-op/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/11-signed-user-op/+page.md @@ -145,7 +145,6 @@ In step 2 we will call the `getUserOpHash` on the `entryPoint` and pass in `user ---- Now that we have this imported, we need to be sure to add the following line to in our contract, just above the `run` function. @@ -403,7 +402,6 @@ Just for the purpose of testing or working on a local chain, we are going to hav ---- 2. Why is `using MessageHashUtils for bytes32` necessary? What does it do? @@ -417,7 +415,6 @@ It allows the function to call `toEthSignedMessageHash` directly on a bytes32 va ---- 3. What is the purpose of the generateSignedUserOperation function in the SendPackedUserOp script? @@ -431,7 +428,6 @@ It generates a signed `PackedUserOperation` by creating an unsigned user operati ---- 4. What is included in the `userOpHash` to prevent cross-chain replay attacks? @@ -445,4 +441,3 @@ It generates a signed `PackedUserOperation` by creating an unsigned user operati ---- diff --git a/courses/advanced-foundry/6-account-abstraction/13-test-validate-user-ops/+page.md b/courses/advanced-foundry/6-account-abstraction/13-test-validate-user-ops/+page.md index 2ffd7eec..fb677c48 100644 --- a/courses/advanced-foundry/6-account-abstraction/13-test-validate-user-ops/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/13-test-validate-user-ops/+page.md @@ -82,7 +82,6 @@ Now we know that we are getting the correct signatures and that our validation i 1. What is the main objective of the testValidationOfUserOps? ----
**Click for Answers** @@ -91,10 +90,8 @@ Now we know that we are getting the correct signatures and that our validation i
---- 2. Why do we set missingAccountFunds to 1e18? ----
**Click for Answers** @@ -103,10 +100,8 @@ Now we know that we are getting the correct signatures and that our validation i
---- 3. What does the assertEq(validationData, 0) statement check for? ----
**Click for Answers** @@ -115,5 +110,4 @@ Now we know that we are getting the correct signatures and that our validation i
---- diff --git a/courses/advanced-foundry/6-account-abstraction/15-advanced-debugging/+page.md b/courses/advanced-foundry/6-account-abstraction/15-advanced-debugging/+page.md index c43f71b6..6b262708 100644 --- a/courses/advanced-foundry/6-account-abstraction/15-advanced-debugging/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/15-advanced-debugging/+page.md @@ -154,7 +154,6 @@ Before we move on, take a look at the review questions. Move on to the next less ---- 2. What modification was made to the vm.getNonce function in the generateSignedUserOperation function? @@ -168,4 +167,3 @@ It was modified to subtract 1 from the nonce value. This change ensures that the ---- diff --git a/courses/advanced-foundry/6-account-abstraction/18-zksync-setup/+page.md b/courses/advanced-foundry/6-account-abstraction/18-zksync-setup/+page.md index 57e841c2..c80fbb17 100644 --- a/courses/advanced-foundry/6-account-abstraction/18-zksync-setup/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/18-zksync-setup/+page.md @@ -121,7 +121,6 @@ Now we have our **ZK Minimal Account** set up. Things are starting to get exciti ---- 2. How are EOAs different in zkSync compared to Ethereum? @@ -135,7 +134,6 @@ In zkSync, EOAs are smart contracts. ---- 3. What are the 4 functions of the IAccount interface? @@ -152,4 +150,3 @@ In zkSync, EOAs are smart contracts. ---- diff --git a/courses/advanced-foundry/6-account-abstraction/19-iaccount/+page.md b/courses/advanced-foundry/6-account-abstraction/19-iaccount/+page.md index d0ae27b8..3c40fbe3 100644 --- a/courses/advanced-foundry/6-account-abstraction/19-iaccount/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/19-iaccount/+page.md @@ -79,7 +79,6 @@ struct Transaction { ---- When we send an Account Abstraction transaction through zkSync, the `Transaction` struct will essentially be populated. This will be our focus for now. The following parameters we won't worry about, for now. But here is the gist of what they do. @@ -164,7 +163,6 @@ This lesson gave us a gist of what our IAccount interface will do. Take a moment ---- 2. What is the role of the executeTransactionFromOutside function? @@ -178,7 +176,6 @@ This lesson gave us a gist of what our IAccount interface will do. Take a moment ---- 3. When is the prepareForPaymaster function called? @@ -192,4 +189,3 @@ This lesson gave us a gist of what our IAccount interface will do. Take a moment ---- diff --git a/courses/advanced-foundry/6-account-abstraction/20-system-contracts/+page.md b/courses/advanced-foundry/6-account-abstraction/20-system-contracts/+page.md index de27fa60..fc5b2f12 100644 --- a/courses/advanced-foundry/6-account-abstraction/20-system-contracts/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/20-system-contracts/+page.md @@ -70,7 +70,6 @@ As always, Let's do some review. Move on to the next lesson when you are ready. --- 1. What are the two phases of sending an account abstraction transaction in zkSync? ----
**Click for Answers** @@ -79,11 +78,9 @@ As always, Let's do some review. Move on to the next lesson when you are ready.
---- 2. What is the role of the ContractDeployer system contract in zkSync? ----
**Click for Answers** @@ -95,11 +92,9 @@ As always, Let's do some review. Move on to the next lesson when you are ready.
---- 3. How does deploying a smart contract on zkSync differ from deploying on Ethereum? ----
**Click for Answers** @@ -108,7 +103,6 @@ As always, Let's do some review. Move on to the next lesson when you are ready.
---- diff --git a/courses/advanced-foundry/6-account-abstraction/21-type-113-lifecycle/+page.md b/courses/advanced-foundry/6-account-abstraction/21-type-113-lifecycle/+page.md index b3524356..0986cad5 100644 --- a/courses/advanced-foundry/6-account-abstraction/21-type-113-lifecycle/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/21-type-113-lifecycle/+page.md @@ -98,7 +98,6 @@ This is essentially what will happen in our transaction lifecycle through the va ---- 2. Who is the msg.sender when validateTransaction is called in zkSync? @@ -112,7 +111,6 @@ This is essentially what will happen in our transaction lifecycle through the va ---- 3. What happens if validateTransaction does not update the nonce? @@ -126,7 +124,6 @@ This is essentially what will happen in our transaction lifecycle through the va ---- 4. What is the role of the bootloader in zkSync? @@ -140,4 +137,3 @@ This is essentially what will happen in our transaction lifecycle through the va ---- diff --git a/courses/advanced-foundry/6-account-abstraction/23-zksync-simulations/+page.md b/courses/advanced-foundry/6-account-abstraction/23-zksync-simulations/+page.md index d2f8ba8f..6d3576d4 100644 --- a/courses/advanced-foundry/6-account-abstraction/23-zksync-simulations/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/23-zksync-simulations/+page.md @@ -244,7 +244,6 @@ It makes a system call to the NonceHolder contract to conditionally increment th ---- 2. Which function in NonceHolder.sol is called to increment the nonce? @@ -258,7 +257,6 @@ It makes a system call to the NonceHolder contract to conditionally increment th ---- 3. What library did we import to help with system contract calls? @@ -272,4 +270,3 @@ It makes a system call to the NonceHolder contract to conditionally increment th ---- diff --git a/courses/advanced-foundry/6-account-abstraction/24-validate-tx/+page.md b/courses/advanced-foundry/6-account-abstraction/24-validate-tx/+page.md index b77bc4ee..60e39d92 100644 --- a/courses/advanced-foundry/6-account-abstraction/24-validate-tx/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/24-validate-tx/+page.md @@ -105,7 +105,6 @@ function encodeHash(Transaction memory _transaction) internal view returns (byte ---- Then we will need to validate the signature on the `Transaction` struct (similar to `PackedUserOperation` from our Ethereum contract.). We will need a few imports. @@ -261,7 +260,6 @@ totalRequiredBalance() ---- 2. What is the purpose of the requireFromBootLoader modifier? @@ -275,7 +273,6 @@ totalRequiredBalance() ---- 3. What is the purpose of the encodeHash function from MemoryTransactionHelper? @@ -289,4 +286,3 @@ totalRequiredBalance() ---- diff --git a/courses/advanced-foundry/6-account-abstraction/25-execute-tx/+page.md b/courses/advanced-foundry/6-account-abstraction/25-execute-tx/+page.md index 83e71479..32e888e0 100644 --- a/courses/advanced-foundry/6-account-abstraction/25-execute-tx/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/25-execute-tx/+page.md @@ -172,7 +172,6 @@ To ensure that the callee is correctly identified as an address or contract bein ---- 2. What does the assembly code in the executeTransaction function do? @@ -186,7 +185,6 @@ To ensure that the callee is correctly identified as an address or contract bein ---- 3. What is the purpose of the requireFromBootLoaderOrOwner modifier? @@ -200,4 +198,3 @@ To ensure that the callee is correctly identified as an address or contract bein ---- diff --git a/courses/advanced-foundry/6-account-abstraction/3-eth-setup/+page.md b/courses/advanced-foundry/6-account-abstraction/3-eth-setup/+page.md index 716add1c..c5c403d2 100644 --- a/courses/advanced-foundry/6-account-abstraction/3-eth-setup/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/3-eth-setup/+page.md @@ -84,7 +84,6 @@ Next, we need to know what to put in our contract. Well, we know that we are wor ---- From this information, we know that we will need some specific functions to make this happen. [Let's head over to the EIP](https://eips.ethereum.org/EIPS/eip-4337) and see what we need. diff --git a/courses/advanced-foundry/6-account-abstraction/7-execute/+page.md b/courses/advanced-foundry/6-account-abstraction/7-execute/+page.md index 7372553d..183baf69 100644 --- a/courses/advanced-foundry/6-account-abstraction/7-execute/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/7-execute/+page.md @@ -93,7 +93,6 @@ It allows the minimal account contract to interact with other dapps by sending t ---- 2. Why do we need the `requireFromEntryPointOrOwner` modifier? @@ -107,7 +106,6 @@ It ensures that only the EntryPoint contract or the owner of the minimal account ---- 3. How does the `receive` function enable our contract to accept payments? @@ -121,6 +119,5 @@ This is a special function in Solidity that allows the contract to accept plain ---- When you are ready, move on to the next lesson. diff --git a/courses/advanced-foundry/6-account-abstraction/9-owner-execute-test/+page.md b/courses/advanced-foundry/6-account-abstraction/9-owner-execute-test/+page.md index 4239dc13..9975b045 100644 --- a/courses/advanced-foundry/6-account-abstraction/9-owner-execute-test/+page.md +++ b/courses/advanced-foundry/6-account-abstraction/9-owner-execute-test/+page.md @@ -302,7 +302,6 @@ It requires the msg.sender to be either the owner or the entry point, and it tak ---- 2. What is the expected behavior of the testNonOwnerCannotExecuteCommands test? @@ -316,4 +315,3 @@ The test should revert if a non-owner (a random user) attempts to execute comman ---- diff --git a/courses/advanced-foundry/7-daos/6-tests/+page.md b/courses/advanced-foundry/7-daos/6-tests/+page.md index d5577824..81977b8c 100644 --- a/courses/advanced-foundry/7-daos/6-tests/+page.md +++ b/courses/advanced-foundry/7-daos/6-tests/+page.md @@ -361,7 +361,6 @@ function testGovernanceUpdatesBox() public { ---- Woo! This is exciting, we're ready to run the test. diff --git a/courses/curve-v1/1-overview/1-introduction/+page.md b/courses/curve-v1/1-overview/1-introduction/+page.md new file mode 100644 index 00000000..51479172 --- /dev/null +++ b/courses/curve-v1/1-overview/1-introduction/+page.md @@ -0,0 +1,31 @@ +This course is an introduction to the Curve V1 AMM. + +We'll start by looking at the prerequisites for this course. You should be an intermediate Solidity developer, and have experience with Foundry. You should also understand the mathematics of constant product AMMs and constant sum AMMs. + +Here are some optional prerequisites for this course: + +* Uniswap V2 and V3 +* A basic understanding of DeFi. +* Python + +In the course, we'll compare Uniswap V2, V3, and Curve V1. We'll also discuss some basic DeFi concepts, such as stablecoins like DAI and USDC. + +A basic understanding of DeFi will help. + +Finally, we have some optional lessons where we'll use Python to show you some of the math of Curve V1. + +These are nice to have, but they're not required to follow along with the course. + +Here are a few reasons why you should take this course: + +* Understanding Curve V1 might give you some ideas for new AMM design. +* It might also help you in audit contests and bug bounties. +* It is a prerequisite to understand Curve V2. + +Here's what we'll learn in this course: + +* The math and algorithms of Curve V1 +* Vyper +* Testing with Foundry + +Curve V1 is written in Vyper, and we'll do a code walkthrough of the Curve V1 contract. You'll gain some experience reading Vyper. There are exercises and solutions written in Foundry, so you'll have hands-on experience testing with Foundry. diff --git a/courses/curve-v1/1-overview/2-lead-instructor/+page.md b/courses/curve-v1/1-overview/2-lead-instructor/+page.md new file mode 100644 index 00000000..9d11ede1 --- /dev/null +++ b/courses/curve-v1/1-overview/2-lead-instructor/+page.md @@ -0,0 +1,66 @@ +## Introduction to Solidity and Vyper + +We'll start with an introduction to the smart contract programming languages Solidity and Vyper. We'll learn about the differences between these languages and what they're commonly used for. + +First, let's see how many people are familiar with Solidity and Vyper. It's worth knowing that Solidity is probably the most popular smart contract programming language. This is because Solidity is used to develop smart contracts for the Ethereum Blockchain. Vyper is another language that's also used for Ethereum smart contracts, but it's less popular. This is because Vyper is a more secure language and can be considered a more secure alternative to Solidity. + +We'll discuss why Vyper is more secure than Solidity as we go through the course. + +Now, let's get started with our first example. In Solidity, the first thing we'll always see is the pragma statement. In the following example, we're targeting Solidity version 0.8.26 and this is a common statement that we'll see in many smart contracts. + +```javascript +pragma solidity ^0.8.26; +``` + +This statement is important because it ensures that the smart contract code is compatible with the specified version of the Solidity compiler. If you are using an incompatible version, your smart contract code will not compile. + +We'll also see that there are various Solidity versions. These versions differ in terms of features and the available syntax, so it's crucial to use the correct version for your project. + +The next thing we'll see is the contract definition. In this case, we're defining a contract named "MyContract." Solidity supports the use of inheritance. + +```javascript +contract MyContract { +} +``` + +This example defines a simple contract named "MyContract" which we can deploy onto a blockchain to store and update state variables and execute functionality. + +Now let's look at a simple Vyper example. + +```javascript +# pragma solidity ^0.8.26 + +from vyper.interfaces import ERC20 + +interface IUniswapV2Pair: + def getReserves() -> tuple: + pass + +interface IUniswapV2Factory: + def getPair(tokenA: address, tokenB: address) -> address: + pass + +def get_pair(factory: address, tokenA: address, tokenB: address) -> address: + """ + Get the UniswapV2 pair address for two tokens. + """ + return IUniswapV2Factory(factory).getPair(tokenA, tokenB) +``` + +This code contains several interesting elements: + +1. The first line indicates that the Vyper compiler version is `0.8.26`. +2. It imports the `ERC20` interface from `vyper.interfaces`. +3. It defines a custom interface `IUniswapV2Pair` to allow interacting with UniswapV2 pair contracts. +4. It defines another interface `IUniswapV2Factory` to interact with UniswapV2 factory contracts. +5. Finally, it defines a function `get_pair` to return the UniswapV2 pair address for two given tokens. + +We can also see that Vyper is a very different language compared to Solidity. + +Let's go through a quick comparison of Solidity and Vyper: + +* **Solidity** is a Turing-complete language which means it's powerful but can be complex and susceptible to errors. +* **Vyper** is a more limited language that can be easier to use and more secure. + +The next topic we'll cover is the concept of smart contracts. + diff --git a/courses/curve-v1/1-overview/3-word-about-repo/+page.md b/courses/curve-v1/1-overview/3-word-about-repo/+page.md new file mode 100644 index 00000000..a6fafca7 --- /dev/null +++ b/courses/curve-v1/1-overview/3-word-about-repo/+page.md @@ -0,0 +1,13 @@ +We will be using a GitHub repository called `advanced-defi-2024`. For the course for Curve V1, we need to scroll down and look for a Markdown file called `curve-v1.md`. + +Click on this file to go to the Markdown file that shows all the links that we will need in this course. This file lists the prerequisites that we have already discussed and the tools that we will need. + +The tools that we will need are: +* Foundry +* Python/Jupyter Lab (optional) + +For each lesson, we need to watch the videos on Cyfrin Updraft. For the exercises, we can find the link under the section called Topics. Scrolling down, we see a section called Topics and then links to exercises and solutions under the sections for each lesson. + +There are other links inside this Markdown file, some of them linked to code used in the videos, others linked to notes and graphs. + +If we have further questions, we can navigate to GitHub discussions under this repository. diff --git a/courses/curve-v1/1-overview/4-setup-repo/+page.md b/courses/curve-v1/1-overview/4-setup-repo/+page.md new file mode 100644 index 00000000..68426780 --- /dev/null +++ b/courses/curve-v1/1-overview/4-setup-repo/+page.md @@ -0,0 +1,37 @@ +To do the exercises in this course, we need to get clone this repo. And to do that, click on Code and then copy this URL. + +Inside the terminal type in: + +```bash +git clone +``` + +and then paste the repo. + +Once the repo is cloned into your computer, next CD into advanced-defi-2024. + +```bash +cd advanced-defi-2024 +``` + +The exercises are written in Foundry, so to do the exercises, we first need to install Foundry. If you have not done so, copy and paste this command to install Foundry: + +```bash +curl -L https://foundry.paradigm.xyz | bash foundrysup +``` + +Now this command only works for Linux. + +Once Foundry is installed, let's do a quick check to make sure that the contracts which are used for the exercises compile. The contracts are located under a folder called Foundry, so say: + +```bash +cd foundry +``` + +And inside here we'll do a quick check by compiling the contract. + +```bash +forge build +``` + +And the contract compiled successfully. So, if you made it this far, congratulations, you are now ready to do the exercises. diff --git a/courses/curve-v1/1-overview/5-ex-sol/+page.md b/courses/curve-v1/1-overview/5-ex-sol/+page.md new file mode 100644 index 00000000..2c069490 --- /dev/null +++ b/courses/curve-v1/1-overview/5-ex-sol/+page.md @@ -0,0 +1,44 @@ +We'll go through an introduction to the exercises and solutions included in this repository. + +The exercises and solutions are located under the `test` folder. +```bash +cd test +``` + +The tests are grouped by DeFi protocol. Each DeFi protocol has an `exercises` and `solutions` folder. +* The `exercises` folder contains starter code for you to write your code. +* The `solutions` folder contains solutions for you to check your code. + +We'll go through an example by executing the code inside the `curve-v1` solutions folder. + +First, we'll set up our `FORK_URL` environment variable. This will be the URL that will be used to execute our code against the main network. + +```bash +FORK_URL=https://eth-mainnet.g.alchemy.com/v2/KzrtPzEzhqNs4Jn_05qM7M4AjS50K4 +``` +The `FORK_URL` I used was from Alchemy. + +Next, let's execute the code inside the `curve-v1` solutions. + +We'll execute the code inside `CurveV1Swap.test.sol`. To do this, we need to run the following command in our terminal. + +```bash +forge test --fork-url $FORK_URL --match-path test/curve-v1/solutions/CurveV1Swap.test.sol --match-test test_exchange --vvv +``` + +The command contains the following: +* `forge test`: The command used to execute the tests. +* `--fork-url $FORK_URL`: Sets the `FORK_URL` environment variable. +* `--match-path test/curve-v1/solutions/CurveV1Swap.test.sol`: Specifies the path to the test file. +* `--match-test test_exchange`: Only runs the `test_exchange` function. +* `--vvv`: Verbose mode, prints extra information. + +We can also change the command to only execute one of the contracts inside the exercises. We'll start with the same command as the previous example and change the path to `test/curve-v1/exercises/CurveV1Swap.test.sol`. + +```bash +forge test --fork-url $FORK_URL --match-path test/curve-v1/exercises/CurveV1Swap.test.sol --match-test test_exchange --vvv +``` + +The `test_exchange` function fails because the code inside the contract is incomplete. It is your task to fill the code inside the contract under the exercises. + +This is how the exercises and solutions are organized inside this repository. diff --git a/courses/curve-v1/1-overview/6-curve-intro/+page.md b/courses/curve-v1/1-overview/6-curve-intro/+page.md new file mode 100644 index 00000000..3db44083 --- /dev/null +++ b/courses/curve-v1/1-overview/6-curve-intro/+page.md @@ -0,0 +1,23 @@ +## Introduction to Curve v1 + +Curve v1 is an automated market maker (AMM) optimized for swapping tokens that have similar prices. This is a DeFi tool designed to minimize slippage and maximize gains for stablecoins and similar assets. + +## Understanding Stablecoins + +Stablecoins are cryptocurrencies pegged to a stable asset, such as the US dollar. This means their value aims to stay close to that asset's value. USDC and DAI are examples of stablecoins that target $1. + +## Swapping USDC and DAI + +We can use Curve v1 to swap USDC and DAI, as both are stablecoins. When swapping 1,000 USDC on Uniswap v2, we might lose $4 due to slippage. However, Curve v1 can offer a better deal, minimizing the loss to less than $1. + +## How Curve v1 Minimizes Slippage + +Curve v1 uses a liquidity pool specifically designed for stablecoins. This pool allows for more efficient swaps, as the underlying assets have a smaller price difference. + +## Example: Swapping USDC and DAI on Curve v1 + +We can see an example of swapping 1,000 USDC on Curve v1. The result is closer to 1,000 DAI, showcasing the minimized slippage. This is possible because Curve v1 is optimized for swapping stablecoins, unlike Uniswap v2 which handles a wider range of assets. + +## Conclusion + +Curve v1 is a valuable tool for DeFi users looking to swap stablecoins and similar assets with minimal slippage. Its optimization for these assets ensures that trades are close to a 1:1 exchange rate, maximizing the return on your investments. diff --git a/courses/curve-v1/1-overview/7-comparison/+page.md b/courses/curve-v1/1-overview/7-comparison/+page.md new file mode 100644 index 00000000..8e3fe15b --- /dev/null +++ b/courses/curve-v1/1-overview/7-comparison/+page.md @@ -0,0 +1,25 @@ +## Comparing Curve V1, Uniswap V2, and Uniswap V3 (Swapping USDC/DAI) + +In this lesson, we will consider the differences between Curve V1, Uniswap V2, and Uniswap V3. Specifically, we will look at these differences in the context of swapping USDC and DAI. + +Let's begin by considering the language used by each AMM. Both Uniswap V2 and Uniswap V3 are written in Solidity. Curve V1, on the other hand, is written in Vyper. + +In the previous video, we saw that Uniswap V2 has high slippage. For example, if we were to swap 1,000 USDC, then we would only receive 996 DAI on Uniswap V2. Uniswap V3 introduces concentrated liquidity, which results in lower slippage. If we were to swap 1,000 USDC, we would likely receive something close to 1,000 DAI. + +Curve V1 also has low slippage. If we were to swap 1,000 USDC on Curve V1, we would receive 999.92 DAI. + +The next difference we will consider is how many tokens can be added to the pools. Both Uniswap V2 and Uniswap V3 support only two tokens. For example, on Uniswap V2, we saw a USDC/DAI pool. Uniswap V3 can also only have two tokens. + +Curve V1 is different. It can have two or more tokens. For example, on the Curve Finance user interface, we can see a pool that has three tokens: DAI, USDC, and USDT. There are also pools with four tokens. + +Another difference between Uniswap AMMs and Curve AMMs is how liquidity is added and removed. In Uniswap V2, when we add liquidity, both tokens must be sent. When we remove liquidity, both tokens will come out. + +Uniswap V3 is a little more complex. Adding liquidity depends on the price range that we are adding liquidity to and the current price. If we add liquidity to a price range outside the current active price, then we can only add one token. However, if we are adding liquidity to a price range that includes the active price, then we must add both tokens. Removing liquidity works the same way. If we are removing liquidity from a position outside the active price, then we can only withdraw one token. However, if our liquidity position is within the active price, then we will get both tokens. + +Curve V1 allows us to specify which tokens we want to withdraw. It can be one token, it can be all tokens, or it can be somewhere in between. For example, if we have 100 LP tokens in a Curve pool that consists of DAI, USDC, and USDT, and we only want to withdraw DAI, then we can do so. We can also withdraw only USDC or USDT. We can also withdraw all of the tokens in a balanced way or specify how we want to withdraw them. For example, we could decide to withdraw only DAI and USDT and not USDC. + +On Uniswap V2 and Uniswap V3, there is no fee charged for adding or removing liquidity. However, on Curve V1, there is a fee charged for imbalance. This means that if we had 100 USDC, 100 DAI, and 100 USDT in a Curve pool and decided to withdraw all of our liquidity in DAI, then a fee would be charged. The same is true when we are adding liquidity. + +The last major difference between Uniswap and Curve V1 is how swap fees are charged. Both Uniswap V2 and Uniswap V3 charge swap fees on token in. However, Curve V1 charges swap fees on token out. + +These are just some of the key differences between Curve V1, Uniswap V2, and Uniswap V3. As you can see, each AMM has its own unique characteristics. diff --git a/courses/curve-v1/2-math/01-graph-eq/+page.md b/courses/curve-v1/2-math/01-graph-eq/+page.md new file mode 100644 index 00000000..9b174641 --- /dev/null +++ b/courses/curve-v1/2-math/01-graph-eq/+page.md @@ -0,0 +1,41 @@ +## Curve B1's AMM Curve + +We are going to cover the Uniswap V2 AMM curve, specifically the B1 curve. The equation for a constant product curve is: + +``` +x * y = k +``` + +We can also graph the constant sum curve using the equation: + +``` +x + y = d +``` + +In this case, instead of using _k_, we will use _d / 2�_. + +The B1 curve equation is a combination of the constant product and constant sum curves. The equation is as follows: + +``` +xy + A(x + y) * (d / 2)� = (d / 2)� + AD� +``` + +The parameter _A_ defines how flat the curve is. As _A_ increases, the curve becomes more flat and looks like the constant sum curve. If _A_ decreases, the curve behaves more like the constant product curve. + +We can also see this by multiplying the constant sum equation by _x_ and _y_: + +``` +xy + A(x + y) * (d / 2)� = (d / 2)� + AD� +``` + +We can further adjust the equation by dividing by _d / 2�_: + +``` +xy / (d / 2)� + A(x + y) = 1 + AD� / (d / 2)� +``` + +We can see that if _x_ and _y_ are equal to _d / 2_, the term _xy / (d / 2)�_ is equal to 1. Conversely, if _x_ and _y_ are not equal to _d / 2_, the term approaches zero. + +When _x_ and _y_ are balanced, the curve behaves like the constant sum curve. However, when _x_ and _y_ are imbalanced, the curve behaves more like the constant product curve. + +In summary, the B1 curve is a combination of constant product and constant sum curves. This gives the curve the ability to behave like either curve depending on the balance of the tokens. diff --git a/courses/curve-v1/2-math/02-graph-xy/+page.md b/courses/curve-v1/2-math/02-graph-xy/+page.md new file mode 100644 index 00000000..6bf51635 --- /dev/null +++ b/courses/curve-v1/2-math/02-graph-xy/+page.md @@ -0,0 +1,49 @@ +In the previous video, we graphed curves onto 3D and we worked with an equation that represented the curve in between a constant sum and a constant product. The equation was x times y divided by d over 2 squared. + +We are going to take a closer look at this equation in 3D. + +When we graph this equation, we get a 3D surface. + +```javascript +f(x,y)=xy/((d/2)^2) +``` + +The inputs are all the points on the x y plane and the output is described in the z-axis. + +The result is a 2D surface in 3D. + +Now, we want to know how this function behaves on the points of the curves V1 curve. + +First, we will plot the equation x times y divided by d over 2 squared for all of the points on the constant product. + +We will take the x and y points that satisfy x times y equals d over 2 squared and then we will evaluate the equation. + +The result is a blue line on the 2D surface. + +```javascript +f(x,y)=xy/((d/2)^2) +``` + +The z-axis value is always 1 for this blue line. + +This is because x times y is always equal to d over 2 squared. + +This function will always evaluate to 1. + +Now, we will look at the points on the constant sum curve. + +This produces an arc on the surface. + +The arc touches 1 and decreases to 0 as y goes to 0, and also decreases to 0 as x goes to 0. + +We can plot a point at (d over 2, d over 2, 1) to see where this equation evaluates to 1. + +We can also see that this orange line approaches 0 as x and y become more unbalanced. + +When we evaluate this equation for the curves V1 curve, we get the orange line. The line represents the z-axis value for each point of the V1 curve. + +The orange line approaches 0 as x and y become more unbalanced. + +We have now analyzed how x times y divided by d over 2 squared behaves in 3D. + +When x and y are close to d over 2, then the equation evaluates to 1. As x and y become more unbalanced, they approach 0. diff --git a/courses/curve-v1/2-math/03-math/+page.md b/courses/curve-v1/2-math/03-math/+page.md new file mode 100644 index 00000000..12020658 --- /dev/null +++ b/courses/curve-v1/2-math/03-math/+page.md @@ -0,0 +1,56 @@ +## Curve V1 Equation + +We'll cover the Curve V1 equation, which can be found in the Curve whitepaper. + +The general equation for *n* tokens is shown below. *n* represents the number of tokens, which can be greater than or equal to 2. + +``` +kai * sum all of the token balances from i = 1 to i = n + multiply all of the token balances from i = 1 to i = n = kai * (D + (D / n) raised to the power of n) +``` +This equation is a combination of the constant sum and constant product. + +We can substitute the *kai* symbol with its corresponding terms, which is *A* multiplied by all of the token balances, divided by *D* / *n*, and then raised to the power of *n*. + +``` +A * sum all of the token balances from i = 1 to i = n / (D / n) raised to the power of n * sum all of the token balances from i = 1 to i = n + multiply all of the token balances from i = 1 to i = n = A * (D + (D / n) raised to the power of n) +``` + +The whitepaper also contains an alternative form of the Curve V1 equation. The form is shown below: + +``` +A * n raised to the power of n * sum all of the token balances from i = 1 to i = n + D = A * D * n raised to the power of n + D raised to the power of n + 1 / product of all coins from i = 1 to i = n +``` +We can derive this alternative form from the general form by dividing both sides of the equation by *D* divided by the product of all the tokens. + +Let's simplify the left side of the equation first: + +``` +A * n raised to the power of n / (D / n) raised to the power of n * sum all of the token balances from i = 1 to i = n + D +``` +*A* and the product of all of the tokens will cancel out on both the top and the bottom. *D* raised to the power of *n* will cancel out with *D* raised to the power of *n - 1* multiplied by *D*. + +This will leave us with: + +``` +A * n raised to the power of n * sum all of the token balances from i = 1 to i = n + D +``` + +Now, let's simplify the right side of the equation: + +``` +A * (D + (D / n) raised to the power of n) / (D / product of all coins from i = 1 to i = n) +``` +The product of all the coins cancels out on both the top and the bottom. *D* raised to the power of *n* will cancel out with *D* raised to the power of *n - 1* multiplied by *D*. + +This will leave us with: + +``` +A * D * n raised to the power of n + D raised to the power of n + 1 / product of all coins from i = 1 to i = n +``` + +Putting the left and right side together, we get: + +``` +A * n raised to the power of n * sum all of the token balances from i = 1 to i = n + D = A * D * n raised to the power of n + D raised to the power of n + 1 / product of all coins from i = 1 to i = n +``` +This is the alternative form of the Curve V1 equation. diff --git a/courses/curve-v1/2-math/04-newton/+page.md b/courses/curve-v1/2-math/04-newton/+page.md new file mode 100644 index 00000000..4a48e79e --- /dev/null +++ b/courses/curve-v1/2-math/04-newton/+page.md @@ -0,0 +1,53 @@ +In this lesson, we'll discuss how Curve utilizes a mathematical technique called Newton's Method to compute the amount of tokens to be exchanged and liquidity. We'll also provide an overview of how Newton's method works. + +## Curve v1 Newton's Method + +Let's start with the simple case of Uniswap v2. For Uniswap v2, the amount of tokens to be received from a swap and the liquidity can be calculated directly from a simple equation: + +```javascript +x * y = L +``` + +where x represents the token being swapped for, and y represents the token to be received. L represents the liquidity. + +For Uniswap v3, there is no easy equation to calculate x, y, or D. + +## Newton's Method + +Newton's Method is a mathematical technique used to find the solution for an equation f(x) = 0. The goal is to find the value of x where the function f(x) will evaluate to zero. + +Here is the algorithm for Newton's Method: + +```javascript +x_n = x_0 + +while True: + x_{n+1} = x_n - f(x_n) / f(x_n) + + if abs(x_{n+1} - x_n) <= some small number: + return x_{n+1} + + x_n = x_{n+1} +``` + +## Using Newton's Method + +Let's take a look at how Newton's Method is used in Curve to calculate y and D. For the following examples, we'll be using the equation for n = 2: + +**Newton's method to find y** + +```javascript +F(y) = x * D(x+y) + x * y - x * D(1/2)^2 +``` + +We use Newton's Method to find the value of y where f(y) = 0. + +**Newton's method to find D** + +```javascript +F(D) = x * D(x+y) + x * y - x * D(1/2)^2 +``` + +We use Newton's Method to find the value of D where f(D) = 0. + +In the next video, we'll go over how to implement these examples using Python. diff --git a/courses/curve-v1/2-math/05-newton-python/+page.md b/courses/curve-v1/2-math/05-newton-python/+page.md new file mode 100644 index 00000000..66e3e23f --- /dev/null +++ b/courses/curve-v1/2-math/05-newton-python/+page.md @@ -0,0 +1,120 @@ +## Newton's Method in Python + +We will work through a couple of examples of using Newton's method in Python. + +### Example 1 + +Let's say we have a simple function given by the following equation: +```python +def f(x): + return x**2 - 2 +``` +and we want to find the value of *x* for which *f(x) = 0*. Newton's method is a numerical method that allows us to approximate the solution to this equation. + +Here's the Python code that implements Newton's method to solve for *x*: +```python +def f(x): + return x**2 - 2 + +def df(x): + return 2*x + +def f_solve(f, df, x0, delta): + for i in range(n): + print(i, x0) + x1 = x0 - f(x0) / df(x0) + if abs(x1 - x0) <= delta: + return x1 + assert False, "cannot find solution" + x0 = x1 + +n = 1000 +delta = 0.0001 +x = f_solve(f, df, 1, n, delta) +print("f(x) =", f(x)) +print("x =", x) +``` + +In this code: + +* `f(x)` is our function, which takes a single input *x* and returns the value of *x� - 2*. +* `df(x)` is the derivative of our function, which returns the value of *2x*. +* `f_solve(f, df, x0, delta)` is the function that implements Newton's method. It takes the function `f`, its derivative `df`, an initial guess `x0`, and a tolerance `delta`. The function iterates until the difference between successive approximations is less than the tolerance. +* `n` is the maximum number of iterations allowed. +* `delta` is the tolerance value. + +**Running the code:** + +We set our initial guess to *1*, the maximum number of iterations to *1,000*, and the tolerance to *0.0001*. When we run this code, we get the following output: + +```bash +python newton_example.py +``` + +The output will show the iterations performed by the code until it finds a solution. The final value of *x* will be approximately *1.41421*, which is the square root of 2. + +### Example 2 + +Now let's say we have a curve defined by the following equation: + +```python +def f(x, y, A, D): + K = A * x * y / (D / 2)**2 + return x * y + K - (D / 2)**2 - K * D**2 +``` + +We know the values of *x*, *A*, and *D*, and we want to find the value of *y* that satisfies this equation. + +To solve for *y*, we'll use Newton's method again. The Python code is similar to the previous example, but we need to define new functions for our curve and its derivative with respect to *y*. Here's the modified code: + +```python +def f(x, y, A, D): + K = A * x * y / (D / 2)**2 + return x * y + K - (D / 2)**2 - K * D**2 + +n = 100 +delta = 0.0001 +x = 120 +y = 90 +A = 10 +D = 200 + +def f_D(d): + return f(x, y, A, d) + +def df_d(d): + return 4 * A * x * y * d / 2 - d / 2 + +d = f_solve(f_D, df_d, D, n, delta) +print("d", d) +print("f_D(d)", f_D(d)) + +def f_y(y): + return f(x, y, A, d) + +def df_dy(y): + return x + 4 * A * x * y * d / 2 - A * x * d * (y * d / 2) / d**2 + +y = f_solve(f_y, df_dy, y, n, delta) +print("y", y) +print("f_y(y)", f_y(y)) +``` + +Here, we've defined: + +* `f_y(y)` is the function for the curve, taking *y* as input and using the known values of *x*, *A*, and *D*. +* `df_dy(y)` is the partial derivative of the curve's function with respect to *y*. + +**Running the code:** + +We'll execute this code and get the following output: + +```bash +python newton_example.py +``` + +The output shows that Newton's method finds a value for *y* that is close enough to satisfy the curve's equation. + +### Conclusion + +Newton's method is a powerful tool for finding solutions to equations. We have seen how to use it in Python, and how to modify it to solve different problems. diff --git a/courses/curve-v1/2-math/06-sympy/+page.md b/courses/curve-v1/2-math/06-sympy/+page.md new file mode 100644 index 00000000..bdddeb6f --- /dev/null +++ b/courses/curve-v1/2-math/06-sympy/+page.md @@ -0,0 +1,46 @@ +We will use Python to calculate partial derivatives for the curve's equation. The library we will use is called `sympy`. + +First we need to import `sympy`: + +```python +from sympy import symbols, diff, simplify, init_printing +init_printing() +``` + +Next, we need to define the curve's equation: + +```python +x, y, D, A = symbols('x y D A') +k = A*x*y*(D**2) / (2*(D**2) + k*D**2) +f = k*x*y + k*x*y * D / 2 +display(f) +``` + +To get the partial derivative of this function with respect to `D` and with respect to `y`, we need to call a function that is provided by the `sympy` library called `diff`. Here we will tell `diff` to differentiate the function `f` with respect to `D`: + +```python +df_dD = diff(f, D) +``` + +Likewise, we will differentiate the function `f` with respect to `y`: + +```python +df_dy = diff(f, y) +``` + +We can then print the results of our differentiation: + +```python +print('df / dD') +print(df_dD) +print('df / dy') +print(df_dy) +``` + +Next, we will discuss a method called Newton's method to solve for `y`: + +```python +y_n = y - f / df_dy +``` + +This is the end of this introductory lesson on partial derivatives. diff --git a/courses/curve-v1/3-contract-overview/01-contract-overview/+page.md b/courses/curve-v1/3-contract-overview/01-contract-overview/+page.md new file mode 100644 index 00000000..ecee9e97 --- /dev/null +++ b/courses/curve-v1/3-contract-overview/01-contract-overview/+page.md @@ -0,0 +1,46 @@ +## Curve V1 Contracts + +We will discuss the contracts that Curve V1 uses to support its automated market maker (AMM). Curve V1 AMMs are a variation of a base contract called StableSwap. + +## StableSwap + +We will discuss the StableSwap contract by looking at examples of pool contracts: 3pool and stETH. + +### 3pool + +The 3pool contract supports three tokens: DAI, USDC, and USDT. The pool contract name is `StableSwap3pool`. + +#### N_COINS + +The `N_COINS` constant is defined as three. This constant is important because we will need to use it when we call a Curve V1 AMM contract. + +#### addLiquidity + +The `addLiquidity` function takes an array of tokens as input. The size of the array is determined by the `N_COINS` constant. We will need to ensure that we provide the correct number of tokens when we call this function. + + +```javascript +def add_liquidity(amounts: uint256[N_COINS], min_amount: uint256) -> uint256: +``` + +### stETH + +The stETH contract supports two tokens: ETH and stETH. The pool contract name is `StableSwap stETH`. + +#### N_COINS + +The `N_COINS` constant is defined as two. + +#### addLiquidity + +The `addLiquidity` function takes an array of tokens as input. The size of the array is determined by the `N_COINS` constant. We will need to ensure that we provide the correct number of tokens when we call this function. + + +```javascript +def add_liquidity(amounts: uint256[N_COINS], min_amount: uint256) -> uint256: +``` + + +## Summary + +Curve V1 uses a contract called StableSwap for its AMMs. The pool contracts are variations of this base contract, such as `StableSwap3pool` and `StableSwap stETH`. Each pool contract holds a specific number of tokens, determined by the `N_COINS` constant. We will need to ensure that we provide the correct number of tokens when we interact with these contracts. diff --git a/courses/curve-v1/3-contract-overview/02-contract-functions/+page.md b/courses/curve-v1/3-contract-overview/02-contract-functions/+page.md new file mode 100644 index 00000000..8f8c284e --- /dev/null +++ b/courses/curve-v1/3-contract-overview/02-contract-functions/+page.md @@ -0,0 +1,13 @@ +The StableSwap contract can hold any number of tokens. For example, the StableSwap3Pool contract manages the tokens DAI, USDC, and USDT. Another example we saw was the STEF4 which holds two tokens, EIF and STEIF. + +We will cover five functions that are common to all StableSwap contracts. These are: + +* exchange +* add\_liquidity +* remove\_liquidity +* remove\_liquidity\_imbalance +* remove\_liquidity\_one\_coin + +The `exchange` function is used to swap tokens. `add_liquidity` is used to add tokens to the pool. + +For removing liquidity, there are several variations. The `remove_liquidity` function will return the liquidity providers all of the tokens. `remove_liquidity_imbalance` will give the user an option to specify how many of each token they wish to get back. `remove_liquidity_one_coin` allows the user to specify the token they want to get back as a single token. diff --git a/courses/curve-v1/3-contract-overview/03-a-and-d/+page.md b/courses/curve-v1/3-contract-overview/03-a-and-d/+page.md new file mode 100644 index 00000000..68797389 --- /dev/null +++ b/courses/curve-v1/3-contract-overview/03-a-and-d/+page.md @@ -0,0 +1,25 @@ +Let's start by understanding a common pattern in the code for Curve v1. We can see that the functions `exchange`, `add_liquidity`, and the `remove_liquidity` functions are called on the StableSwap3Pool contract. + +The pattern we'll see in all the functions is that they first calculate the `A` parameter, and then they calculate the liquidity `D`. + +Let's examine the `exchange` function. We can see that it first normalizes the token balances, transfers the token in, updates the token balance, and then calculates the token out balance. After that, it calculates the `A` parameter, the liquidity `D`, calculates the token out, calculates the swap fee, updates the token balances, and then transfers the token out. + +The `add_liquidity` function has a similar pattern. It calculates an imbalance fee multiplier, calculates `A`, calculates the liquidity `D0`, transfers the tokens in, updates the token balances, calculates liquidity `D1`, calculates the imbalance fee, calculates liquidity `D2`, updates the token balances, calculates the liquidity provider shares, and then mints the liquidity provider's LP shares. + +The `remove_liquidity` function is a little different. It first calculates the token out amounts, updates the token balances, and then transfers the tokens out. Finally, it burns the LP shares. + +The `remove_liquidity_one_coin` function first calculates `dy` and the imbalance fee, calculates `A`, calculates `D`, calculates the imbalance fee, updates the token balances, burns the LP shares, and then transfers the token out. + +So, why do we see a pattern of calculating the `A` parameter before calculating the liquidity `D`? + +We can see that the `A` parameter is a fixed number, but an admin can decide to set a new `A` parameter. Over time, as the `A` parameter changes, the liquidity `D` will also change. + +Let's take a look at an example graph of Curve v1's equation. In this graph, we are looking at the equation for two tokens. We can see that the `x0` and `y0` values must be on this curve. + +This curve will adjust as tokens are swapped. For example, if token `x` goes out and token `y` comes in, the point will move up and to the left. Conversely, if token `x` comes in and token `y` goes out, the point will move down and to the right. + +Let's now see what happens when the `A` parameter changes. In this graph, we can see that the current token balance is represented by a blue dot. An admin could decide to update the `A` parameter to a higher number. + +We can see that the token balance is no longer on the curve. To get the blue dot back on the curve, we need to change the liquidity `D` so that the curve will go through the token balances. + +We see, then, that the code first calculates the `A` parameter and then calculates the `D` parameter. This is because the code needs to find the liquidity `D` that will satisfy the equation given the `A` parameter and the token balances. diff --git a/courses/curve-v1/3-contract-overview/04-code-walk-a/+page.md b/courses/curve-v1/3-contract-overview/04-code-walk-a/+page.md new file mode 100644 index 00000000..5e199237 --- /dev/null +++ b/courses/curve-v1/3-contract-overview/04-code-walk-a/+page.md @@ -0,0 +1,45 @@ +## The A parameter inside curve B1's AMM + +The A parameter inside Curve B1's AMM equation controls how flat the curve is. For example, if we increase A, we can see that the pink line, which represents Curve B1's AMM curve, becomes more flat like the constant sum. On the other hand, when we decrease this A parameter, we see that the curve becomes more like the constant product. When A is equal to zero, it matches the constant product curve. + +Let's take a look at how this A parameter is calculated inside Curve B1's AMM. The contract that we will take a look at is called StableSwap3Pool. This is a contract that is deployed on the main net, and it holds DAI, USDC, and USDT. The function that calculates the current A parameter is called _A. The owner of this contract can set a new A parameter. When a new A parameter is set, it doesn't take effect immediately. Over time, it gradually changes from the current A parameter to this new target A parameter that is set by the admin. + +So, inside this code, you see T1 and A1. A1 is the new A parameter that is set by the admin, and T1 is the timestamp where this new A parameter will stay fixed. At and after timestamp T1, this A parameter will stop increasing and it will stay flat as the new value A1. Let's say that the admin sets the A parameter to A1 at time T0. Then, over time, from time between T0 to T1, the current A will gradually increase to A1. After time T1, since the current A parameter has reached A1, it will stay flat as A1. And we can see this in the code over here. + +```javascript +if block.timestamp < t1: + A0: uint256 = self.initial_A + t0: uint256 = self.initial_A_time + # Expressions in uint256 cannot have negative numbers, thus "if" + # A1 > A0: + if A1 > A0: + return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0) + else: + return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0) +else: + # when t1 == 0 or block.timestamp >= t1 + return A1 +``` + +Let's say that the admin sets the A parameter to A1 at time T0. Then, over time, from time between T0 to T1, the current A will gradually increase to A1. After time T1, since the current A parameter has reached A1, it will stay flat as A1. And we can see this in the code over here. + +```javascript +if block.timestamp < t1: + A0: uint256 = self.initial_A + t0: uint256 = self.initial_A_time + # Expressions in uint256 cannot have negative numbers, thus "if" + # A1 > A0: + if A1 > A0: + return A0 + (A1 - A0) * (block.timestamp - t0) / (t1 - t0) + else: + return A0 - (A0 - A1) * (block.timestamp - t0) / (t1 - t0) +else: + # when t1 == 0 or block.timestamp >= t1 + return A1 +``` + +To understand this code, consider the two cases: When timestamp is equal to T0 and when timestamp is equal to T1. Let's start with the case when timestamp is equal to T0. When timestamp is equal to T0, this expression T0 - T0 is equal to zero. So, when we multiply this part by zero, this whole expression will be equal to zero, so we get A0. On the other hand, when timestamp is equal to T1, then this part of the code is equal to T1 - T0 divided by T1 - T0 is equal to one. So, this part of the code will be A1 - A0 multiplied by one, which is simply equal to A1 - A0. And then let's now do this A0 plus A1 minus A0. The A0 cancels out with this A0 over here, and we get back A1. + +At time T0, we see that the A parameter returns A0, and at time T1, the A parameter returns A1. And anywhere in between, this block.timestamp - T0 divided by T1 - T0 is a fraction that is less than one. So, the A parameter that is returned will be some number between A0 and A1. And that's how this part of the code works. + +This part of the code works almost exactly the same, except the current A parameter gradually decreases to the new parameter A1. diff --git a/courses/curve-v1/3-contract-overview/05-code-walk-xp/+page.md b/courses/curve-v1/3-contract-overview/05-code-walk-xp/+page.md new file mode 100644 index 00000000..5b255e17 --- /dev/null +++ b/courses/curve-v1/3-contract-overview/05-code-walk-xp/+page.md @@ -0,0 +1,60 @@ +## Introduction to the _xp Function + +In Curve.fi AMMs, token balances are converted to have 18 decimals before any calculations. We can see this in a contract for DAI, USDC, and USDT. + +DAI has 18 decimals, so no conversion is necessary. USDC and USDT both have six decimals, so they must be converted to have 18 decimals. + +This is done by an internal function called `_xp`. The naming convention of this function can be remembered as "X" for the token balances and "P" for precision. + +The _xp function converts the token balances within a contract to have 18 decimals. + +Let's take a look at the code: + +```javascript +def _xp(balances: uint256(N_COINS)) -> uint256(N_COINS): + result: uint256(N_COINS) = RATES + for i in range(N_COINS): + # DAI = 1e18 * dai balance / 1e18 = 18 decimals + # USDC = 1e30 * usdc balance / 1e18 = 18 decimals + # USDT = 1e30 * usdt balance / 1e18 = 18 decimals + result[i] = result[i] * self.balances[i] / LENDING_PRECISION + return result +``` + +The first line of the function creates an array called `result`. It initializes the array to the value of `RATES`. + +```javascript +result: uint256(N_COINS) = RATES +``` + +The function then uses a for loop to iterate through each coin in the contract. + +```javascript +for i in range(N_COINS): +``` + +The _xp function is used to normalize the token balances in the contract. We can see this in the function, where it takes each balance, multiplies it by the corresponding rate, and divides by `LENDING_PRECISION`. + +```javascript +result[i] = result[i] * self.balances[i] / LENDING_PRECISION +``` + +We'll go over how the calculation works step by step: + +The first iteration of the loop, `result` will hold the value of `1e18`, which is the rate for DAI. The function then multiplies this by the DAI balance in the contract. DAI has 18 decimals, so this will not change the number of decimals. The result is then divided by `LENDING_PRECISION`, which is 10^18. This results in the DAI balance with 18 decimals. + +The second iteration of the loop, `result` will hold the value of `1e30`, which is the rate for USDC. The function then multiplies this by the USDC balance in the contract. USDC has six decimals, so multiplying by `1e30` will result in 36 decimals. The result is then divided by `LENDING_PRECISION`, which is 10^18. This results in the USDC balance with 18 decimals. + +The third iteration of the loop, `result` will hold the value of `1e30`, which is the rate for USDT. The function then multiplies this by the USDT balance in the contract. USDT has six decimals, so multiplying by `1e30` will result in 36 decimals. The result is then divided by `LENDING_PRECISION`, which is 10^18. This results in the USDT balance with 18 decimals. + +The final line of the function returns the `result` array. + +```javascript +return result +``` + +**Note**: It's important to remember that the _xp function is only used internally. When you interact with the Curve.fi AMM contract, you will not need to call this function. The contract will handle the conversion of token balances automatically. + +## Summary + +In summary, the _xp function takes the token balances in the contract and normalizes them to have 18 decimals. This is done by multiplying each balance by the corresponding rate and dividing by `LENDING_PRECISION`. diff --git a/courses/curve-v1/3-contract-overview/06-code-walk-get-d/+page.md b/courses/curve-v1/3-contract-overview/06-code-walk-get-d/+page.md new file mode 100644 index 00000000..d8dbf47a --- /dev/null +++ b/courses/curve-v1/3-contract-overview/06-code-walk-get-d/+page.md @@ -0,0 +1,109 @@ +We are going to talk about the value D in an AMM and how it's calculated. + +When we talk about a perfectly balanced pool, the amount of each token will be D/N, where N is the number of tokens. For example, if we have 50 tokens, each token will be 10 tokens. This is represented in the first line of the following code: + +```javascript +D = 50 +``` + +This is just an example, and you can change the value of D. + +We are using a StableSwap 3 Pool contract, where D is calculated by using Newton's method. + +The function, `get_D` takes the number of coins in the pool, `N_COINS`, and an amount, `amp`, which represents the amplification coefficient in the StableSwap 3 pool. Let's have a look at this code: + +```javascript +@internal +@pure +def get_D(xp: uint256[N_COINS], amp: uint256) -> uint256: + # sum(x_i) + S: uint256 = 0 + for x in xp: + S += x + if S <= 0: + return 0 + Dprev: uint256 = 0 + D: uint256 = S + Ann: uint256 = amp * N_COINS + for i in range(255): + # D^(N+1) / (N * prod(x_i)) + D_P: uint256 = D * D / (N * prod(xp[i])) + for x in xp: + # D * x * D / (L * N_COINS) # If division by 0, this will be bogus + D_P = D_P * x * D / (x * N_COINS) + Dprev = D + # equivalent to D - f(D) / (df/dD)(D) + D = (Ann * S + D_P * N_COINS) * D / (Ann * (N - 1) * D + (N_COINS + 1) * D_P) + # Equality with the precision of 1 + if D > Dprev: + if D - Dprev <= 1: + break + else: + if Dprev - D <= 1: + break + return D +``` + +The first part of this code sums up all the normalized token balances. This is performed within this `for` loop: + +```javascript +for x in xp: + S += x +``` + +If this sum is less than or equal to zero, we return zero: + +```javascript +if S <= 0: + return 0 +``` + +If the sum is greater than zero, we continue with the calculation. + +Next, the code uses Newton's method to calculate the value of D. We use this `for` loop to iterate a maximum of 255 times. + +```javascript +for i in range(255): +``` + +The code then calculates the value of D_P, which is equivalent to the expression D^(N+1) / (N * prod(x_i)), and it will use this value of D_P for the rest of the loop. + +```javascript +D_P: uint256 = D * D / (N * prod(xp[i])) +``` + +The code then iterates through the token balances to calculate a new value of D_P. + +```javascript +for x in xp: + D_P = D_P * x * D / (x * N_COINS) +``` + +The new value of D is calculated based on the previous value of D, Dprev, and the new values of D_P. + +```javascript +D = (Ann * S + D_P * N_COINS) * D / (Ann * (N - 1) * D + (N_COINS + 1) * D_P) +``` + +This equation is equivalent to this equation: D - f(D) / (df/dD)(D) + +Finally, the code checks if the difference between the new value of D and the previous value of D is less than or equal to 1. + +```javascript +if D > Dprev: + if D - Dprev <= 1: + break +else: + if Dprev - D <= 1: + break +``` + +If the difference is less than or equal to one, the code will break out of the for loop and return the current value of D, which has been calculated using Newton's method. + +```javascript +return D +``` + +If Newton's method does not converge, the code will complete the 255 iterations and return the final value of D. + +As you can see, the code is written in Python, and this function will return a value D that satisfies the curve B1 equation for the given parameters. \ No newline at end of file diff --git a/courses/curve-v1/3-contract-overview/07-code-walk-get-virtual-price/+page.md b/courses/curve-v1/3-contract-overview/07-code-walk-get-virtual-price/+page.md new file mode 100644 index 00000000..b2eae062 --- /dev/null +++ b/courses/curve-v1/3-contract-overview/07-code-walk-get-virtual-price/+page.md @@ -0,0 +1,34 @@ +## The `get_virtual_price` Function + +In this lesson, we're going to break down the `get_virtual_price` function. We'll learn what the function calculates and how it uses other functions to achieve this. + +The function looks like this: + +```javascript +@view +@external +def get_virtual_price() -> uint256: + """ + Returns portfolio virtual price (for calculating profit) + scaled up by 1e18 + """ + D: uint256 = self.get_D(self.xp(), self.A()) + # D is in the units similar to DAI (e.g. converted to precision 1e18) + # When balanced, D / N = x / u - total virtual value of the portfolio + # token_supply: uint256 = self.token.totalSupply() + return D * PRECISION / token_supply() +``` + +Let's break down what each line of code does. + +First, we see the function takes the value of `D`. In previous lessons, we learned that this value is calculated using the function `get_D`. We can use this knowledge to understand how the function works. + +`D` represents the liquidity of the AMM. When all the token balances inside the AMM are equal, the pool is perfectly balanced. We can say that if there are `N` tokens, then `D / N` will be the token balance for each token. + +Therefore, `D` is the liquidity of the pool. + +Next, the function retrieves the `token_supply`. This represents the amount of LP tokens that were minted. + +After obtaining the value of `D` and the `token_supply`, the function multiplies `D` by `PRECISION` and divides the result by the `token_supply`. This means that the function `get_virtual_price` is calculating the value of each LP token. + +In summary, the `get_virtual_price` function calculates the virtual price of each LP token within the AMM. It achieves this by first calculating the liquidity of the pool (`D`) and then dividing that value by the total amount of minted LP tokens. diff --git a/courses/curve-v1/3-contract-overview/08-code-walk-calc-token-amount/+page.md b/courses/curve-v1/3-contract-overview/08-code-walk-calc-token-amount/+page.md new file mode 100644 index 00000000..ca09733a --- /dev/null +++ b/courses/curve-v1/3-contract-overview/08-code-walk-calc-token-amount/+page.md @@ -0,0 +1,16 @@ +Let's take a look at the `calc_token_amount` function in a StableSwap pool contract. This function takes in an array of token amounts and a boolean flag called `deposit`. If `deposit` is true, this means we're adding liquidity. Otherwise, we're removing liquidity. The function returns a single number, a `uint256`. However, this number does not directly represent the amount of tokens being minted or burned. + +This function is a simplified calculation of token supply. It does not take into account fees that might occur when the pool is imbalanced, whether this is from adding or removing liquidity. + +Here's a step-by-step breakdown of the function: + +1. First, the function gets the current balance of the tokens in the pool and assigns it to the variable `balances`. +2. Next, it gets the parameter `A`. +3. The function then calculates the value for `D0`, which represents the initial liquidity in the pool. This is calculated from the current balance of tokens. +4. It then runs a loop to update the `balances` based on whether the input is a deposit or withdrawal. + * If it is a deposit, the loop adds the input `amounts` to the current `balances`. + * If it is a withdrawal, the loop subtracts the input `amounts` from the current `balances`. +5. After the loop has run, the function calculates the value for `D1`, representing the new liquidity in the pool, again based on the current token balances. +6. Finally, the function calculates the difference between `D1` and `D0`, and then divides this difference by the initial liquidity, `D0`. We multiply this by the total supply of LP tokens, to calculate the amount of LP tokens that will be either minted or burned. + +This calculation gives us an approximation of the LP tokens that will be minted or burned. It is a simplified calculation that does not take fees into account. diff --git a/courses/curve-v1/4-swap/01-contract-call-exchange/+page.md b/courses/curve-v1/4-swap/01-contract-call-exchange/+page.md new file mode 100644 index 00000000..eb8fd202 --- /dev/null +++ b/courses/curve-v1/4-swap/01-contract-call-exchange/+page.md @@ -0,0 +1,7 @@ +To swap tokens with the Curve's B1AMM, we will call the function `exchange`. + +For example, let's say that this user wanted to swap DAI for USDC from the StableSwap3Pool contract. + +First, we will call the function `exchange` specifying that the token in is DAI and the amount of DAI to swap. + +The StableSwap3Pool contract will transfer the DAI in from the user over to the pool contract, calculate the amount of USDC to get back and then transfer the USDC from the pool contract over to the user. diff --git a/courses/curve-v1/4-swap/02-code-outline-exchange/+page.md b/courses/curve-v1/4-swap/02-code-outline-exchange/+page.md new file mode 100644 index 00000000..a3081f42 --- /dev/null +++ b/courses/curve-v1/4-swap/02-code-outline-exchange/+page.md @@ -0,0 +1,28 @@ +## Introduction to the exchange function + +We're going to take a walk-through of the *exchange* function in the *StableSwap* pool. + +### Exchange Function Walk-through + +The *exchange* function has the following steps: + +1. **Normalizes token balances (memory)** + This is the first thing the function does. It makes sure all token balances have 18 decimals, regardless of the actual token's decimal places. +2. **Transfer token in (memory)** + The *exchange* function will then transfer the token *in* to the pool. +3. **Update token in balance (memory)** + The normalized balance of the token *in* is stored in memory. +4. **Calculate token out balance** + * **Calculate A** + * **Calculate D** + The function will calculate the new balance of the token *out*. +5. **Calculate token out** + The difference between the new token *out* balance and the old token *out* balance is calculated. +6. **Calculate swap fee** + The *exchange* function calculates the swap fee which is taken out of the token *out*. +7. **Update token balances (storage)** + This step updates the token balances in storage to reflect the changes. +8. **Transfer token out** + Finally, the *exchange* function transfers the token *out* to the user. + +This walk-through provides a basic understanding of the *exchange* function. \ No newline at end of file diff --git a/courses/curve-v1/4-swap/03-code-walk-exchange/+page.md b/courses/curve-v1/4-swap/03-code-walk-exchange/+page.md new file mode 100644 index 00000000..6d5938da --- /dev/null +++ b/courses/curve-v1/4-swap/03-code-walk-exchange/+page.md @@ -0,0 +1,52 @@ +We are going to walk through the `exchange` function that is used to swap tokens on a Curve B1AMM. + +### The `exchange` Function + +The `exchange` function takes in the following parameters: + +* `i`: The index of the token coming in. +* `j`: The index of the token going out. +* `dx`: The amount of the token coming in. +* `min_dy`: The minimum amount of token you expect to get back. If this is not met, the function will fail. + +### Normalizing Token Balances + +First, we will create an array called `rates`. This is a constant value for the `StableSwap3Pool.vy` contract and is set to `1e18, 1e30, 1e30`. + +We then store the balances of the tokens in an array called `old_balances`. The `old_balances` array will contain the balances of `DAI`, `USDC`, and `USDT`. + +Then, the function will normalize the balances by calling the `_xp_mem` function. + +### Transferring Tokens + +Next, we will transfer the token in into the pool contract by calling the `transferFrom` function, which is encoded using a `raw_call`. + +The `raw_call` is used instead of the ERC20 interface because some tokens, like USDT, do not return a boolean when the `transferFrom` function is called. + +We then get the address of the token that will come in and store it in a variable called `input_coin`. We also check if the token has a fee on transfer. If it does, we calculate the fee by calling the `balanceOf` function on the input token. + +The difference between the balance before and after transferring the token will be the actual amount that came into the pool. + +### Calculating the Amount Out and Fee + +We then perform some calculations to determine the amount of token that will go out and the associated fee. We will call these variables `dy` and `dy_fee`. + +* `x`: This represents the normalized balance of the token coming in. +* `xp`: This is the normalized balance of the token going out. +* `y`: This is the new normalized balance of the token going out, calculated by calling the `getY` function. + +We then subtract the new normalized balance from the current normalized balance to get `dy`. We then subtract the amount of the fee from the `dy` and store the result in `dy_fee`. + +### Updating Token Balances + +Next, we will update the token balances to reflect the changes that have occurred. We are basically just adding the amount that came in and subtracting the amount going out and the admin fee. + +* The balance of the token that came in is updated by adding the actual amount that came in, `dx_w_fee`. +* The balance of the token going out is updated by subtracting the amount that went out, `dy`. +* The admin fee is calculated as a fraction of the `dy_fee`. + +Finally, we log the token exchange event. + +### Summary + +In essence, the `exchange` function takes in tokens, normalizes the balances, transfers the tokens in and then out, and then updates the balances of the tokens. diff --git a/courses/curve-v1/4-swap/04-code-walk-get-y/+page.md b/courses/curve-v1/4-swap/04-code-walk-get-y/+page.md new file mode 100644 index 00000000..d3cce8be --- /dev/null +++ b/courses/curve-v1/4-swap/04-code-walk-get-y/+page.md @@ -0,0 +1,51 @@ +We're going to take a look at the `get_y` function in the StableSwap pool, which is part of Curve v1. + +The `get_y` function takes in the indexes of the token in and token out, along with the new balance of token in and the normalized balances of all tokens except the token out. + +The function then uses Newton's method to calculate the new balance of the token out. To do this, it calculates various variables like `amp`, `D`, `c`, `S_`, `Ann`, and `b`. + +We're going to focus on the equation used for calculating the `c` variable: + +```javascript +c = D**(n+1) / (n**n * p) +``` + +Where: + +* `D` represents the liquidity of the pool. +* `n` is the number of tokens in the pool. +* `p` is the product of the normalized balances of all tokens except the token out. + +We can also see how the `S_` variable is calculated: + +```javascript +S_ = sum(x[i] for i != j) +``` + +Which is the sum of all token balances except the token out. + +Finally, the function calculates `b` using: + +```javascript +b = (Ann * S_ + D * Ann) / Amm - D +``` + +Where: + +* `Ann` represents `a` * `n` to the `n`. +* `Amm` represents `a` * `n` to the `n` * `n`. + +These variables `c`, `D`, `S_`, and `b` are used to calculate the new balance of the token out. + +The function then runs a for loop, iterating a maximum of 255 times. In each iteration, it uses Newton's method to update the value of `y`, which represents the new balance of the token out. + +The equation used in the for loop is: + +```javascript +y = (y * y * c) / (2 * y * b - D) +``` + +This equation is equivalent to Newton's method for finding the root of a function. + +The loop continues to update `y` until the difference between the new `y` and the previous `y` is less than or equal to one. When this condition is met, the function returns the value of `y`, which represents the calculated new balance of the token out. + diff --git a/courses/curve-v1/4-swap/05-code-walk-get-dy/+page.md b/courses/curve-v1/4-swap/05-code-walk-get-dy/+page.md new file mode 100644 index 00000000..6f855bf1 --- /dev/null +++ b/courses/curve-v1/4-swap/05-code-walk-get-dy/+page.md @@ -0,0 +1,61 @@ +The function that we will use to calculate the amount of tokens we will receive from a swap with a Curve B1 AMM is called `get_dy`. There is another function called `get_dy_underlying` that has the same purpose but behaves differently. + +The difference between these two functions makes sense when the tokens inside the pool contract are yield bearing. + +For example, for a three pool, the tokens are DAI, USDC, and USDT. If you were to hold these tokens, they would not give you any yield. In this case, calling the functions `get_dy` and `get_dy_underlying` will give you back the exact same amounts. + +Now, let's consider the case when the tokens inside the pool earn some kind of yield. For example, let's say we have the tokens DAI and cUSDC. The token cUSDC is a token that you get for depositing USDC into Compound. This token is yield bearing. If you were to hold on to this cUSDC over time, you will earn some interest. + +Let's say you wanted to find out how much USDC you will get for putting in DAI. The pool tokens are DAI and cUSDC. In this case, to answer the question of how much USDC we will get for putting in DAI, we will call the function `get_dy_underlying`. This function will calculate how much USDC we will get if we were to put in DAI. + +For a stable swap three pool, the tokens are DAI, USDC, and USDT. The functions `get_dy_underlying` and `get_dy` are the same. + +Let's take a look at the function `get_dy`. As input, it's going to take the index of the token in and the index of the token out, and the amount of tokens you are going to put in, dx. Then, it calculates the amount of tokens that the AMM will give you back. + +First, it gets the rates. Remember for this stable swap three pool, the rates are a fixed number of 1e18, 1e30, and 1e30. Next, it gets the token balances, which are normalized to all have 18 decimals. The tokens are DAI, USDC, and USDT. DAI has 18 decimals, but USDC and USDT have six decimals. By calling this function, all of the token balances are normalized with 18 decimals. + +The next part of the code calculates the amount of tokens that will come out, and the fees on tokens out. + +```javascript +def get_dy(i: int128, j: int128, dx: uint256) -> uint256: + # dx and dy in c-units + rates: uint256[N_COINS] = RATES + xp: uint256[N_COINS] = self.xp() + x: uint256 = xp[i] + (dx * rates[i]) // PRECISION + y: uint256 = self.get_y(i, j, x, xp) + dy: uint256 = (xp[j] - y - 1) * PRECISION // rates[j] + _fee: uint256 = self.fee * dy // FEE_DENOMINATOR + return dy - _fee +``` + +The first part is adding the amount of token in to the current balance of token in, inside this pool. + +```javascript +x: uint256 = xp[i] + (dx * rates[i]) // PRECISION +``` + +This part normalizes the amount of token in to have 18 decimals. Next, it calculates the pool balance of token out, if we were to put in the dx amount of tokens into this pool. Remember that this function uses the Newton's method to calculate the new balance of token out. + +```javascript +y: uint256 = self.get_y(i, j, x, xp) +``` + +Since token is coming in, the balance of token out will decrease. Taking the difference of the current balance of token out, and the new balance of token out, accounting for rounding errors, we also minus one. Basically, this difference of the old balance of token out, minus the new balance of token out, is the calculation for the amount of token that will come out. This is named dy. + +```javascript +dy: uint256 = (xp[j] - y - 1) * PRECISION // rates[j] +``` + +A fraction of this dy will be kept inside the pool as swap fee. And this is calculated by taking some fraction of dy. + +```javascript +_fee: uint256 = self.fee * dy // FEE_DENOMINATOR +``` + +The actual amount of tokens that will come out is dy minus the fee. + +```javascript +return dy - _fee +``` + +So, that's the function `get_dy`. diff --git a/courses/curve-v1/4-swap/06-exercise-1-get-dy-underlying/+page.md b/courses/curve-v1/4-swap/06-exercise-1-get-dy-underlying/+page.md new file mode 100644 index 00000000..3e794eef --- /dev/null +++ b/courses/curve-v1/4-swap/06-exercise-1-get-dy-underlying/+page.md @@ -0,0 +1,44 @@ +## Exercise 1: Calculating the Amount of USDC for a Swap + +Let's start with the first exercise where we'll use a function called `get_dy_underlying` to determine the amount of USDC we'll receive after swapping in 1,000,000 DAI. + +The function `get_dy_underlying` has three inputs: + +* **Index of the token we are putting in** +* **Index of the token we are getting out** +* **The amount of the token we are putting in** + +**To complete this exercise, we need to write code that calls the `get_dy_underlying` function with the appropriate inputs, and use the returned value to calculate the amount of USDC we'll get.** + +First, we can initialize a `uint256` variable to store the returned value: +```javascript +uint256 dy = 0; +``` + +Then, we need to call the function `get_dy_underlying` with the correct indexes. In this case, DAI is token index 0 and USDC is token index 2. We'll also put in 1,000,000 DAI: +```javascript +dy = get_dy_underlying(0, 2, 1e6 * 1e18); +``` + +Now that we have the calculated amount of USDC in our `dy` variable, we can log it to the console for confirmation: +```javascript +console.log("dy %e", dy); +``` + +To verify that our code works, we can add an assertion that the calculated amount of USDC (the `dy` variable) is a valid USDC amount. You can find the correct USDC amount for a 1,000,000 DAI swap in the example file. + +Here's the full code: +```javascript +function test_get_dy_underlying() public { + // Calculate swap from DAI to USDC + // Write your code here + uint256 dy = 0; + dy = get_dy_underlying(0, 2, 1e6 * 1e18); + console.log("dy %e", dy); + assertGT(dy, 0, "dy = 0"); +} +``` + +Remember that this example is using the `get_dy_underlying` function from `IStaleSwap3Pool.sol`. If you need a refresher on how to call this function, you can find it in the `/src/interfaces/curve/IStaleSwap3Pool.sol` file. + +Let's move on to Exercise 2! diff --git a/courses/curve-v1/4-swap/07-solution-1-get-dy-underlying/+page.md b/courses/curve-v1/4-swap/07-solution-1-get-dy-underlying/+page.md new file mode 100644 index 00000000..8ebd0ff7 --- /dev/null +++ b/courses/curve-v1/4-swap/07-solution-1-get-dy-underlying/+page.md @@ -0,0 +1,160 @@ +## Intro to Curve.fi Pool Interactions + +In this lesson, we will learn how to interact with a Curve.fi pool. Specifically, we will learn how to use the function `get_dy_underlying()`. + +Let's look at our contract: +```javascript +// Exercise 1 +// Call get_dy_underlying to calculate the amount of USDC for swapping +// 1,000,000 DAI to USDC +function test_get_dy_underlying() public { + // Calculate swap from DAI to USDC + // Write your code here + uint256 dy = 0; + + console.log("dy %e", dy); + assertGT(dy, 0, "dy = 0"); +} +``` + +The first step is to copy the interface of the `IStableSwap3Pool` contract from the `IStableSwap3Pool.sol` file. +```javascript +interface IStableSwap3Pool { + function get_dy_underlying(int128 i, int128 j, uint256 dx) + external + view + returns (uint256 dy); + + function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) + external; + + function add_liquidity(uint256[3] calldata coins, uint256 min_lp) + external; + + function remove_liquidity(uint256 lp, uint256[3] calldata min_coins) + external; + + function remove_liquidity_one_coin(uint256 lp, int128 i, uint256 min_coin) + external; + + function calc_withdraw_one_coin(uint256 lp, int128 i) + external + view + returns (uint256 coin); +} +``` + +We can then paste this interface into the `CurveV1Swap.test.sol` file: +```javascript +// Exercise 1 +// Call get_dy_underlying to calculate the amount of USDC for swapping +// 1,000,000 DAI to USDC +function test_get_dy_underlying() public { + // Calculate swap from DAI to USDC + // Write your code here + get_dy_underlying(int128 i, int128 j, uint256 dx) + uint256 dy = 0; + + console.log("dy %e", dy); + assertGT(dy, 0, "dy = 0"); +} +``` + +Now, we need to call the `get_dy_underlying()` function from our `CurveV1Swap.test.sol` contract. + +First, we need to access the `IStableSwap3Pool` contract using the `pool` variable we defined earlier: +```javascript +// Exercise 1 +// Call get_dy_underlying to calculate the amount of USDC for swapping +// 1,000,000 DAI to USDC +function test_get_dy_underlying() public { + // Calculate swap from DAI to USDC + // Write your code here + pool.get_dy_underlying(int128 i, int128 j, uint256 dx) + uint256 dy = 0; + + console.log("dy %e", dy); + assertGT(dy, 0, "dy = 0"); +} +``` + +Next, we need to pass the following three parameters into the function: +1. **`i`**: The index of the token we are swapping from (DAI). +2. **`j`**: The index of the token we are swapping to (USDC). +3. **`dx`**: The amount of DAI we are swapping. + +From our pool definition: +```javascript +IStableSwap3Pool private constant pool = IStableSwap3Pool(CURVE_3POOL); +``` + +And the pool composition (DAI, USDC, USDT): +```javascript +IERC20 private constant dai = IERC20(DAI); +IERC20 private constant usdc = IERC20(USDC); +IERC20 private constant usdt = IERC20(USDT); +``` + +We can see that DAI has index 0, USDC has index 1, and USDT has index 2. So, let's update our code: +```javascript +// Exercise 1 +// Call get_dy_underlying to calculate the amount of USDC for swapping +// 1,000,000 DAI to USDC +function test_get_dy_underlying() public { + // Calculate swap from DAI to USDC + // Write your code here + pool.get_dy_underlying(0, 1, uint256 dx) + uint256 dy = 0; + + console.log("dy %e", dy); + assertGT(dy, 0, "dy = 0"); +} +``` + +Now we need to define our `dx` variable. We are swapping 1 million DAI. As DAI has 18 decimals, this is represented by: +```javascript +1e6 * 1e18 +``` + +Let's update our code to incorporate this. +```javascript +// Exercise 1 +// Call get_dy_underlying to calculate the amount of USDC for swapping +// 1,000,000 DAI to USDC +function test_get_dy_underlying() public { + // Calculate swap from DAI to USDC + // Write your code here + pool.get_dy_underlying(0, 1, 1e6 * 1e18); + uint256 dy = 0; + + console.log("dy %e", dy); + assertGT(dy, 0, "dy = 0"); +} +``` + +Finally, we can assign the returned value of our `pool.get_dy_underlying()` function to the `dy` variable. +```javascript +// Exercise 1 +// Call get_dy_underlying to calculate the amount of USDC for swapping +// 1,000,000 DAI to USDC +function test_get_dy_underlying() public { + // Calculate swap from DAI to USDC + // Write your code here + uint256 dy = pool.get_dy_underlying(0, 1, 1e6 * 1e18); + + console.log("dy %e", dy); + assertGT(dy, 0, "dy = 0"); +} +``` + +Now let's execute our test. In our terminal, we can use Foundry's `forge test` command with the following arguments: +* **`--fork-url`**: This argument specifies the RPC endpoint for our blockchain. +* **`--match-test`**: This argument helps us execute specific tests. We will match against the name of our test function: `test_get_dy_underlying`. +* **`--dbv`**: This argument tells Foundry to use a persistent database for our forked blockchain. + +The complete command is: +```bash +forge test --fork-url $FORK_URL --match-test test_get_dy_underlying --dbv +``` + +We can see that our test passes. This means that we have successfully called the `get_dy_underlying()` function and calculated the amount of USDC we will receive when swapping 1 million DAI. diff --git a/courses/curve-v1/4-swap/08-exercise-2-exchange/+page.md b/courses/curve-v1/4-swap/08-exercise-2-exchange/+page.md new file mode 100644 index 00000000..eedc6a6d --- /dev/null +++ b/courses/curve-v1/4-swap/08-exercise-2-exchange/+page.md @@ -0,0 +1,52 @@ +## Introduction to Curve.fi on Ethereum + +In this lesson, we'll learn how to swap DAI for USDC on Curve.fi. + +### The Swap Function + +We will call the `exchange()` function in the `IStableSwap3Pool` interface to perform the swap. + +### The `exchange()` function + +The `exchange()` function takes in the following parameters: + +* `i`: The index for the token in. +* `j`: The index for the token out. +* `dx`: The amount of token in that you're going to put in. +* `min_dy`: The minimum amount of token out that you expect to get back. + +### Let's get started + +To call the `exchange()` function to swap DAI for USDC, we can navigate to the `IStableSwap3Pool.sol` file under the "interfaces/curve" directory. + +```bash +cd interfaces/curve +``` + +The file contains the `exchange()` function that we will use. + +```javascript +function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external; +``` + +The `exchange()` function is part of the `IStableSwap3Pool` interface. The `IStableSwap3Pool` interface is defined in the `IStableSwap3Pool.sol` file. + +We can import the `IStableSwap3Pool` interface into our test file so that we can use the `exchange()` function. + +```javascript +import "./src/interfaces/curve/IStableSwap3Pool.sol"; +``` + +We will import the `DAI`, `USDC`, and `USDT` constants from the `constants.sol` file. + +```javascript +import (DAI, USDC, USDT, CURVE_3POOL) from "./src/constants.sol"; +``` + +We can call the `exchange()` function from our test file to perform a swap. + +```javascript +pool.exchange(DAI, USDC, dx, min_dy); +``` + +We will cover calling the `exchange()` function in a later lesson. diff --git a/courses/curve-v1/4-swap/09-solution-2-exchange/+page.md b/courses/curve-v1/4-swap/09-solution-2-exchange/+page.md new file mode 100644 index 00000000..9ae50334 --- /dev/null +++ b/courses/curve-v1/4-swap/09-solution-2-exchange/+page.md @@ -0,0 +1,15 @@ +In this lesson, we will be going over the solution for Exercise 2. + +Exercise 2 is to swap 1 million DAI for USDC on Curve B1. + +The function that we will need to call is: +```javascript +pool.exchange() +``` +Let's check the interface and then inside the interface, it's going to take in these inputs. I'll copy these inputs and then we'll paste it here. And before I forget, I'll put a semicolon in as well. + +Okay, so for the inputs, the first input is the index of the token that we are putting in. We are swapping DAI for USDC, and DAI has index 0. USDC has index 1. The amount of DAI that we are going to put in is 1 million DAI. 1e6 * 1e18. 1 million has 6 zeros, as you can see from here, and DAI has 18 decimals. 1e6 multiplied by 1e18 represents 1 million DAI. Okay. Next minimum DY, minimum amount of USDC that we expect to get back. For this example, let's say 999,000. 999,000 is 0.999 * 1e6. 0.999 of 1 million is 999,000. And USDC has 6 decimals, so say 1e6. + +That completes Exercise 2. Let's execute this test. Inside the terminal, I'll type `forge test --fork-url {FORK_URL} --match-path test/curve-v1/exercises/CurveV1Swap.test.sol --match-test test_exchange --vvv`. + +Okay. And our test passed. For putting in 1 million DAI, we got 999,000.999177995475e11 USDC. diff --git a/courses/curve-v1/5-add-liquidity/01-contract-call-add-liq/+page.md b/courses/curve-v1/5-add-liquidity/01-contract-call-add-liq/+page.md new file mode 100644 index 00000000..aadb1608 --- /dev/null +++ b/courses/curve-v1/5-add-liquidity/01-contract-call-add-liq/+page.md @@ -0,0 +1,24 @@ +## Introduction to Adding Liquidity + +Adding liquidity to a Curve pool is a process where a user provides tokens to a pool in exchange for LP shares. These LP shares represent the user's ownership in the pool. + +### Overview of Adding Liquidity + +To add liquidity, a user will call the `add_liquidity` function in the Curve contract. This function requires the user to specify the amount of each token they want to provide to the pool. The amount of tokens can be any combination of the tokens in the pool, with the amount of one or more of the tokens being equal to zero. For example, if a user wants to add only DAI to the pool, they can set the amount of USDC and USDT to zero. + +In the example of the StableSwap3 pool, there are three tokens: DAI, USDC, and USDT. A user can choose to add any combination of these tokens to the pool. + +### The Process of Adding Liquidity + +1. **Call the Function**: The user calls the `add_liquidity` function in the Curve contract. + +2. **Specify Token Amounts**: The user specifies the amount of each token they want to provide to the pool. + +3. **Transfer Tokens**: The tokens specified by the user are transferred to the Curve pool contract. + +4. **Calculate LP Shares**: The Curve pool contract calculates the amount of LP shares to mint to the user based on the amount of tokens provided. + +5. **Mint LP Shares**: The Curve pool contract mints LP shares to the user. + +These LP shares represent the user's ownership of the liquidity in the pool. The owner of the LP shares can later withdraw any of the tokens in the pool by exchanging their LP shares back to the pool contract. + diff --git a/courses/curve-v1/5-add-liquidity/02-code-outline-add-liq/+page.md b/courses/curve-v1/5-add-liquidity/02-code-outline-add-liq/+page.md new file mode 100644 index 00000000..31ee52a3 --- /dev/null +++ b/courses/curve-v1/5-add-liquidity/02-code-outline-add-liq/+page.md @@ -0,0 +1,45 @@ +## add_liquidity + +We'll quickly outline the function `add_liquidity` before we look at the code. + +The first step inside the `add_liquidity` function is to calculate the `imbalance_fee_multiplier`. This is how we discourage users from adding too much of one token, potentially causing the pool to become imbalanced. + +The function then calculates the `A` parameter, which is a parameter of the pool. + +Next, we calculate the current liquidity of the pool, which we refer to as `D0`. + +After this, the function transfers the tokens into the pool. + +Then the token balances are updated. At this stage, this happens in memory rather than updating the state variables, which saves gas. + +Next, we calculate the liquidity again, now with the transferred tokens added, and we call this `D1`. + +Now that we have `D0` and `D1`, we calculate the imbalance fee. + +After calculating the imbalance fee, we once again calculate the liquidity, this time after removing the imbalance fee, which we call `D2`. + +Finally, we update the token balances, this time updating the state variables, because we now have the final balances, and we need to update the pool's state. + +Finally, we calculate the LP shares for the liquidity provider and mint LP tokens. + +## Code Block Examples + +Here's an example of the `D0` calculation: +```javascript +function calculateLiquidityD0(tokens) { + let D = 0; + for (let i = 0; i < tokens.length; i++) { + D = D + tokens[i]; + } + return D; +} +``` + +And an example of the code that updates the token balances in the pool's storage: +```javascript +function updateTokenBalances(tokenBalances, updatedBalances) { + for (let i = 0; i < tokenBalances.length; i++) { + tokenBalances[i] = updatedBalances[i]; + } +} +``` \ No newline at end of file diff --git a/courses/curve-v1/5-add-liquidity/03-imbalance-fee/+page.md b/courses/curve-v1/5-add-liquidity/03-imbalance-fee/+page.md new file mode 100644 index 00000000..79317d27 --- /dev/null +++ b/courses/curve-v1/5-add-liquidity/03-imbalance-fee/+page.md @@ -0,0 +1,102 @@ +## Imbalance Fee + +The imbalance fee is a fee that is charged when a user adds or removes liquidity from a pool in a way that changes the ratio of the tokens in the pool. This fee is designed to incentivize users to maintain a balanced pool, which helps to ensure that the pool is always liquid and that the price of the tokens is stable. + +To understand how the imbalance fee is calculated, we can walk through a visual example. Let's assume that we have a StableSwap pool with the following token balances: + +* DAI: 100 +* USDC: 90 +* USDT: 110 + +We can calculate the liquidity of the pool (D0) as follows: + +``` +D0 = calc_D([100, 90, 110], [1, 1, 1], [0, 0, 0], A, [0, 0, 0]) +``` + +D0 in this case is 299. + +To determine the ideal balances for each token if the pool were perfectly balanced, we can divide D0 by 3: + +* DAI: 299/3 = 100 +* USDC: 299/3 = 100 +* USDT: 299/3 = 100 + +Now, let's say that a user adds 300 DAI to the pool. This will increase the DAI balance to 400. To calculate the new ideal balances, we need to first calculate the new liquidity (D1): + +``` +D1 = calc_D([400, 90, 110], [1, 1, 1], [0, 0, 0], A, [0, 0, 0]) +``` + +D1 in this case is 581. + +The ideal balances for each token if the pool were perfectly balanced with the new liquidity are: + +* DAI: 581/3 = 194 +* USDC: 581/3 = 194 +* USDT: 581/3 = 194 + +The imbalance fee is calculated by taking the difference between the actual token balances and the ideal token balances, and then multiplying that difference by the fee multiplier. + +For example, the imbalance fee for DAI would be calculated as follows: + +* Difference: 400 - 194 = 206 +* Imbalance Fee: 206 * fee = 2.06 + +The fee multiplier in this example is 1%, so the imbalance fee for DAI would be 2.06 DAI. + +We would perform the same calculation for USDC and USDT: + +* USDC: (90 - 194) * fee = 8.4 +* USDT: (110 - 194) * fee = 8.1 + +The actual token balances after the imbalance fee is deducted would be: + +* DAI: 400 - 2.06 = 397.94 +* USDC: 90 - 0.84 = 89.16 +* USDT: 110 - 1.03 = 108.97 + +We can use Python to calculate the imbalance fee for this example: + +```python +def calc_xD(x0, x1, x2, A, D0): + # ... + return # result of the calculation + +# Initial balances +b = [100, 90, 110] +# Adding 300 DAI +b1 = [b[0] + 300, b[1], b[2]] + +# Initial liquidity +D0 = calc_D(b[0], b[1], b[2], A, [0, 0, 0]) + +# Liquidity after adding liquidity +D1 = calc_D(b1[0], b1[1], b1[2], A, D0) + +# Imbalance fee +diffs = [D1 * b[i] / D0 for i in range(len(b))] +fees = [abs(d - b[i]) * fee for i, d in enumerate(diffs)] +``` + +We can then plot these results on a graph to visualize the imbalance fee: + +```python +import matplotlib.pyplot as plt + +# ... +plt.show() +``` + +To experiment with the Python script, you need to: + +1. Install Jupyter Labs and Python by running these commands in your terminal: + ```bash + pip install jupyterlab + ``` + ```bash + pip install python + ``` +2. Navigate to the project repo and open the `curve-v1_imbalance-fee.ipynb` file in Jupyter Labs. +3. Run the script and experiment with different values for the token balances, the amount of tokens added, and the fee multiplier. + diff --git a/courses/curve-v1/5-add-liquidity/04-code-walk-add-liq/+page.md b/courses/curve-v1/5-add-liquidity/04-code-walk-add-liq/+page.md new file mode 100644 index 00000000..60326745 --- /dev/null +++ b/courses/curve-v1/5-add-liquidity/04-code-walk-add-liq/+page.md @@ -0,0 +1,17 @@ +## Adding Liquidity + +The function `add_liquidity` is called to add tokens into Curve V1's AMM. For the inputs, it's going to take in the amount of coins to put in and the minimum amount of LP token to mint. `min_mint_amount`. + +Next, it initializes some variables: `fees` is an array that will be used later for imbalance fee. Curve V1 allows you to add liquidity in a way that makes the token balances imbalanced. For example, you can decide to put only one token in, or you can decide to put all tokens in. And when you add one token as liquidity, this will make the current pool balance imbalanced. So that is what this `fees` array is used for. We'll come back to this later. + +Next, we have a variable called `_fee`. This number is used to charge for an imbalance fee. If you take a look at this, it has a weird formula: `fee * N_COINS / (4 * (N_COINS - 1))`. Basically, what this does is it makes sure that when you add liquidity, and then remove liquidity, it won't be like swapping tokens without any swap fee. Let me explain this further. Let's say that we have token X, and then we swap, and then we get back token Y. In this case, we will have a swap fee on token Y. + +Now, in Curve V1, we can also add liquidity in such a way that makes the token balances of the pool imbalanced. For example, we can decide only to add one token. So, let's say that we add X token to the pool. X token add liquidity. Now, Curve V1 also allows us to remove liquidity in such a way that makes the token balances of the pool imbalanced. So, for example, after we had only one token as liquidity, let's say that we remove liquidity, and we remove liquidity in another coin. Say remove liquidity, and we request token Y out. So effectively, what we did here was we put in X token and then, we took out token Y. This is the same as swapping token X for token Y. + +Now, if we do not have fee on imbalance fee when we're adding liquidity, Basically, this allows us to swap tokens for free. And to make sure that this does not happen, this weird equation makes sure that when we add liquidity and then remove liquidity, it is basically like a swap. For the math for how to derive this part of the equation, I'll put a link in the course. So, that's what this fee is used for. And then we get the admin fee and then calculate the `_A`. + +Next, we get the `token_supply`. This will be the total supply of LP tokens that are minted. And then, we first calculate liquidity D0. D0 will be the liquidity before we add any tokens into this pool. And we can see this over here. D0 is equal to `get_D_mem(old_balances, amp)`. `old_balances` are the token balances of this pool. And then, we initialize an array called `new_balances`, which is basically copying the old balances. Then, the next part of the code deals with transferring in the tokens. So, scrolling down, we can see that inside the for loop, we get the coins to put in as liquidity. And then, we call the function `transferFrom`. And notice that here we're using `raw_call`. This is to go around the problem that USDT does not return any Boolean when `transferFrom` is called. If you were to use the ERC20 interface for `transferFrom`, when we call `transferFrom` on USDT, this part of the code will fail. So, to avoid this problem, it's using a `raw_call` to call `transferFrom`. So, for each iteration, it's going to transfer the tokens in. And then, it updates this `new_balances`. Remember, this `new_balances` was initialized as `old_balances`. So, to the old balances, we add some amount, the amount that came in. And with this `new_balances`, next we calculate the D again. This time it is called D1. Again, this D represents liquidity. We added some new tokens. And here, what it's doing is it's asking: with this `new_balances`, what is the new liquidity. The next part of the code is calculating the imbalance fee. Imbalance fee basically means that if we were to add tokens into this pool in such a way that does not change the token balance ratios, then we've added tokens in an ideal way. Otherwise, if the ratios of the token change when we had liquidity, then there is some kind of fee that we will have to pay. So, the first question is: well, what is the ideal balance to add as liquidity? And the answer is simple. We take the `old_balances` and we look at D0. This will be the liquidity before adding liquidity, and then D1, this will be the liquidity after adding liquidity. D1 divided by D0 will be the increase in liquidity. So, the old balance should increase by this much amount, D1 divided by D0. As an example, let's say that D1 is 110. D0 is 100. So, D1 divided by D0, so the ideal new balance will be 1.1 times the old balance. + +Next, we calculate how much the new balance is off from this ideal balance. If the ideal balance is greater than `new_balances` of I, then the difference is ideal balance minus `new_balances` of I. Otherwise, it's the opposite: `new_balances` of I minus ideal balance. Here, we're just getting the difference of ideal balance and `new_balances` of I. And then, fees will be equal to this magic number fee that we saw over here, and then multiplies it by the difference, and then divides it by the fee denominator. This is the imbalance fee that will be charged for each token. Looking at this equation, let's imagine the case that we added liquidity so that the difference will be equal to zero. This will mean that the `new_balances` is exactly equal to the ideal balance. And when we execute this part of the equation, we see that the difference will be equal to zero. So, the fee for adding liquidity, in this case, the fee will be equal to zero. + +The next part of the code is to update the balance of the tokens. The balance of tokens will be the new balance and then a fraction of the imbalance fee will be given to the admin. This is what this part of the code is doing: Take the imbalance fee multiplied by some ratio which are given to the admin. Here, admin means the owner of this contract. And then, from this `new_balances`, we minus the fees, and using this `new_balances`, we calculate the liquidity again, D2. And the last part of the code is to mint LP based on the increase from D0 to D2. And you can see this over here. `token_supply` multiplied by D2 minus D0. That's the difference between D2 and D0, and then divided by D0. And then, it checks that this LP amount is greater than the `min_mint_amount` specified by the user. And then, it actually mints the LP tokens. So, that is the function `add_liquidity`. diff --git a/courses/curve-v1/5-add-liquidity/05-exercise-1-add-liq/+page.md b/courses/curve-v1/5-add-liquidity/05-exercise-1-add-liq/+page.md new file mode 100644 index 00000000..a969ed4f --- /dev/null +++ b/courses/curve-v1/5-add-liquidity/05-exercise-1-add-liq/+page.md @@ -0,0 +1,47 @@ +## Adding Liquidity to Curve V1 + +In this lesson, we will learn how to add liquidity to a Curve V1 Automated Market Maker (AMM). + +We'll use a Solidity test contract and the `StableSwap3Pool` interface to demonstrate this process. + +Let's start by understanding the setup of our test contract: + +```javascript +function setUp() public { + deal(DAI, address(this), 1e6 * 1e18); + dai.approve(address(pool), type(uint256).max); +} +``` + +In the `setUp` function, we first use the `deal` function to give our test contract 1 million DAI. + +Then, we use the `approve` function to give the `pool` contract permission to spend all of the DAI held by our test contract. + +Now, we can move on to the actual liquidity adding process. To add liquidity to Curve V1, we need to call the `addLiquidity` function on the `StableSwap3Pool` interface. + +This function takes two inputs: an array of the amount of tokens we want to add and the minimum amount of LP tokens we expect to receive back. + +Here's the relevant part of the `StableSwap3Pool` interface: + +```javascript +function add_liquidity(uint256[3] calldata coins, uint256 min_lp) external; +``` + +The `coins` array specifies the amount of each token we want to add. In our case, the tokens are DAI, USDC, and USDT, so the array will have three elements. + +The `min_lp` parameter is a safety measure to ensure that we receive at least a certain amount of LP tokens in return for the liquidity we add. + +Let's look at how we would call this function in our Solidity test contract: + +```javascript +function test_add_liquidity() public { + uint256 lpBal = lp.balanceOf(address(this)); + assertGt(lpBal, 0); +} +``` + +This code first gets the current LP balance of our test contract using the `balanceOf` function. + +We then use the `assertGt` function to ensure that the LP balance is greater than zero. + +This completes the basic process of adding liquidity to a Curve V1 AMM. We can further refine this process by specifying the exact amount of each token we want to add and by adjusting the `min_lp` parameter to suit our needs. diff --git a/courses/curve-v1/5-add-liquidity/06-solution-1-add-liq/+page.md b/courses/curve-v1/5-add-liquidity/06-solution-1-add-liq/+page.md new file mode 100644 index 00000000..bde1c7c5 --- /dev/null +++ b/courses/curve-v1/5-add-liquidity/06-solution-1-add-liq/+page.md @@ -0,0 +1,33 @@ +In this lesson, we'll learn how to add liquidity to the Curve V1 pool. + +We'll need to call the addLiquidity function. To call this function, we'll navigate to the IStableSwap3Pool interface and copy the addLiquidity function. + +We'll then paste the function into our code. + +```javascript +add_liquidity(uint256[3] calldata coins, uint256 min_lp); +``` + +We'll need to pass in parameters for the function. We'll start by creating a uint256 array of size three with the balances of the coins to add liquidity. + +We'll call this array coins and set the first element to 1 million DAI. The other two elements will be zero, since we're only adding DAI. + +```javascript +uint256[3] memory coins = [uint256(1e6 * 1e18), uint256(0), uint256(0)]; +``` + +We'll also need to prepare a minimum LP (min_lp) parameter. This is the minimum amount of LP tokens we want to receive in return for the liquidity we provide. For simplicity, we'll set this value to one. + +```javascript +pool.add_liquidity(coins, 1); +``` + +Now we can call our function to add liquidity to the Curve V1 pool. + +In the terminal, we'll run the command: + +```bash +forge test --fork-url $FORK_URL --match-path test/curve-v1/exercises/CurveV1AddLiquidity.test.sol --vvv +``` + +This will run our test and show us any console logs that are printed. diff --git a/courses/curve-v1/6-remove-liquidity/01-contract-call-remove-liq/+page.md b/courses/curve-v1/6-remove-liquidity/01-contract-call-remove-liq/+page.md new file mode 100644 index 00000000..1252866d --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/01-contract-call-remove-liq/+page.md @@ -0,0 +1,23 @@ +## Remove Liquidity: Contract Call + +We will look at the first variation of removing liquidity. To do this, we will call the function `remove_liquidity`. + +This function will burn the liquidity provider's LP shares. + +The pool will then send back all of the tokens that are in the pool. The amounts will be proportional to the amount of LP tokens that were burnt. These tokens will be sent back to the liquidity provider. + +For example, let's say we have a user who calls `remove_liquidity` on the StableSwap 3Pool contract. This contract contains three tokens: + +* DAI +* USDC +* USDT + +The user will specify the amount of LP shares that they want to burn. + +The StableSwap contract will then calculate the amount of each of these tokens: + +* DAI +* USDC +* USDT + +that the user is entitled to. It will then burn the shares and send the tokens back to the user. diff --git a/courses/curve-v1/6-remove-liquidity/02-code-outline-remove-liq/+page.md b/courses/curve-v1/6-remove-liquidity/02-code-outline-remove-liq/+page.md new file mode 100644 index 00000000..f39f09be --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/02-code-outline-remove-liq/+page.md @@ -0,0 +1,10 @@ +## Removing Liquidity from a Pool + +We're going to look at the function `remove_liquidity`, which allows users to withdraw their tokens from a pool. There are four main steps involved in this process: + +1. **Calculating Token Amounts**: The first step is to determine the amount of tokens the user will be withdrawing. The amount will depend on the user's provided LP shares. +2. **Updating Token Balances (Storage)**: Next, the state variables that track the token balances for the pool are updated to reflect the removal of liquidity. +3. **Transfer Tokens Out**: The calculated token amounts are then transferred to the user's address. +4. **Burn LP Shares**: Finally, the LP shares associated with the removed liquidity are burned. + +The function `remove_liquidity` allows users to recover their invested capital from a pool. This process is crucial for maintaining a dynamic and liquid market by enabling users to manage their positions. diff --git a/courses/curve-v1/6-remove-liquidity/03-contract-call-remove-liq-one-coin/+page.md b/courses/curve-v1/6-remove-liquidity/03-contract-call-remove-liq-one-coin/+page.md new file mode 100644 index 00000000..5d562c9f --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/03-contract-call-remove-liq-one-coin/+page.md @@ -0,0 +1,11 @@ +## Remove Liquidity One Coin + +Another common way to remove liquidity is to call the function `remove_liquidity_one_coin`. + +This function will burn the LP shares of the user and then send back a single token specified by the user. + +For example, on the StableSwap3Pool contract, a user could call `remove_liquidity_one_coin` and specify an amount of LP shares to burn. The user could then specify a single token they want all of their liquidity withdrawn in. Let's say for example, they want all of their liquidity withdrawn in DAI. + +The StableSwap contract will calculate the amount of DAI to send back to the user, and then transfer this DAI over to the user. + +The difference between this function and the function `remove_liquidity` is that `remove_liquidity` will send back all three tokens to the user, whereas `remove_liquidity_one_coin` will send back just the single token specified by the user. diff --git a/courses/curve-v1/6-remove-liquidity/04-code-outline-remove-liq-one-coin/+page.md b/courses/curve-v1/6-remove-liquidity/04-code-outline-remove-liq-one-coin/+page.md new file mode 100644 index 00000000..d91cedd8 --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/04-code-outline-remove-liq-one-coin/+page.md @@ -0,0 +1,19 @@ +## Removing Liquidity as a Single Coin + +We will walk through how the `remove_liquidity_one_coin` function operates. + +The first step is to calculate the amount of token to be removed. In the code, this is named `dy`. This calculation also calculates the imbalance fee. + +The function that calculates `dy` and the imbalance fee can be difficult to understand. This function is called `calc_withdrawal_one_coin`. + +Here are three things that the `calc_withdrawal_one_coin` function does: + +* Calculate the `A` parameter +* Calculate the liquidity `D` +* Calculate the imbalance fee + +Since we are removing liquidity as a single coin, this will have the effect of making the token balances on the pool imbalanced. + +Next, we need to update the state variables that keep track of the token balances. After updating the balances, we burn the LP shares that the user specified. + +The final step is to transfer the tokens out. diff --git a/courses/curve-v1/6-remove-liquidity/05-code-walk-remove-liq/+page.md b/courses/curve-v1/6-remove-liquidity/05-code-walk-remove-liq/+page.md new file mode 100644 index 00000000..5e995f24 --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/05-code-walk-remove-liq/+page.md @@ -0,0 +1,71 @@ +## Removing Liquidity from a Curve AMM + +Let's discuss the function for removing liquidity from a Curve AMM. + +There are several options when it comes to removing liquidity from Curve's AMM. + +* If you want to remove liquidity in a balanced way, you will be returned all of the tokens in the pool, proportional to your LP tokens. +* You can also remove liquidity by specifying the amount of tokens to remove. +* Lastly, you can remove only one coin as liquidity. + +The function we'll look at first is called `removeLiquidity()`. It's going to take in `amount`, which will be the amount of LP tokens to burn, and `minAmounts`, which will be the minimum amounts of tokens to return. + +```javascript +@external +@nonreentrant('lock') +def remove_liquidity(amount: uint256, min_amounts: uint256[N_COINS]): +``` + +First, it caches the state variable `total_supply`. This will be the total supply of LP tokens. + +```javascript +total_supply: uint256 = self.token.totalSupply() +``` + +Next, it initializes an array called `amounts` and `fees`. + +```javascript +amounts: uint256[N_COINS] = empty(uint256[N_COINS]) +fees: uint256[N_COINS] = empty(uint256[N_COINS]) # Fees are unused but we've got them historically in events +``` + +When we remove liquidity by calling this function, there is no imbalance fee, since all of the tokens will be removed in proportion to the LP tokens. + +Inside the for loop, we can see that it calculates each `value` to be sent by taking the balance of the coins, multiplying by the LP tokens to be burned, and dividing by `total_supply`. + +```javascript +for i in range(N_COINS): + value: uint256 = self.balances[i] * amount / total_supply + assert value >= min_amounts[i], "Withdrawal resulted in fewer coins than expected" +``` + +Then it checks that the `value` to be sent to the caller is greater than or equal to the minimum amount specified by the caller. + +```javascript + self.balances[i] -= value + amounts[i] = value +``` + +It updates the balance, then stores the `value` to be sent in an array called `amounts`. + +```javascript +_response: Bytes[32] = raw_call( + self.coins[i], + concat( + method_id("transfer(address,uint256)"), + convert(msg.sender, bytes32), + convert(value, bytes32), + ), + max_outsize=32, +) +``` + +The next part of the code actually transfers the tokens to the message sender for the amount `value` that was calculated earlier. It also emits an event called `RemoveLiquidity`. + +```javascript + assert convert(_response, bool), "dev: failed transfer +self.token.burnFrom(msg.sender, amount) # dev: insufficient funds +log RemoveLiquidity(msg.sender, amounts, fees, total_supply, amount) +``` + +This function is called if you want to burn your LP tokens and in return, receive all of the tokens inside the pool in the ratio of LP tokens that you burned. diff --git a/courses/curve-v1/6-remove-liquidity/06-graph-calc-withdraw-one-coin/+page.md b/courses/curve-v1/6-remove-liquidity/06-graph-calc-withdraw-one-coin/+page.md new file mode 100644 index 00000000..9384b741 --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/06-graph-calc-withdraw-one-coin/+page.md @@ -0,0 +1,27 @@ +## Introduction to Liquidity Removal in One Coin + +When we want to withdraw liquidity from a Curve pool, we have the option to withdraw all liquidity in a single coin, or just one coin at a time. This lesson will explore how liquidity removal in one coin is calculated. + +Let's say we have a StableSwap 3pool with the following tokens: +* DAI +* USDC +* USDT + +If we want to remove our liquidity in one coin, we can use the `remove_liquidity_one_coin` function. This function takes the following parameters: + +* `token_amount`: The amount of LP tokens to burn +* `i`: The index of the token to withdraw + +To calculate the amount of tokens that will be sent back to us, we can use the `calc_withdraw_one_coin` function. This function will: + +1. **Get the current D.** +2. **Solve the AMM equation y(x) for D - token_amount.** +3. **Calculate the amount of the desired token to withdraw.** + +Let's use a visual example to understand this concept. + +Imagine we have a Curve v1 AMM with a total liquidity (D) of 20. Let's say the current token balances are X = 6.57 and Y = 13.52, as shown by the orange dot on the graph. If we decide to reduce our liquidity by 50%, we will decrease D to 10. Since we're only removing liquidity in one coin, let's say token Y, the token X balance should remain the same. As shown in the graph, the ideal token balance after the removal of liquidity in token Y will be X = 6.57 and Y = 6.76, as shown by the green dot on the graph. Notice that we have decreased the total liquidity by 50% (from D = 20 to D = 10). The new token balance on the curve is the intersection of the vertical line that extends from the original token balance of token X and the new liquidity curve, as shown by the pink line. + +The difference between the original token Y balance and the new balance will give us the amount of token Y that the user will receive. + +The actual amount of tokens that the user will receive will be slightly less than the ideal amount due to fees. Fees will be deducted from the amount of tokens calculated by the `calc_withdraw_one_coin` function. diff --git a/courses/curve-v1/6-remove-liquidity/07-code-walk-remove-liq-one-coin/+page.md b/courses/curve-v1/6-remove-liquidity/07-code-walk-remove-liq-one-coin/+page.md new file mode 100644 index 00000000..96995cc7 --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/07-code-walk-remove-liq-one-coin/+page.md @@ -0,0 +1,48 @@ +## Removing Liquidity + +We can call a function to remove liquidity from the pool. If we want to remove liquidity in one coin, then we can call the `remove_liquidity_one_coin()` function. + +For example, inside the StableSwap3Pool.vy, the tokens are DAI, USDC, and USDT. If we call the function `remove_liquidity_one_coin()`, then it will give us back all three tokens. + +Let's say that we wanted to just withdraw USDC. Then, we would call `remove_liquidity_one_coin()`, and then specify the token that we want to withdraw. + +For the inputs, it's going to take in the `token_amount`. This will be the LP token to be burnt. + +It's going to take in the `index` of the token to withdraw, and the minimum amount of token that you expect to receive. + +```javascript +def remove_liquidity_one_coin(token_amount: uint256, i: int128, min_amount: uint256): +``` + +Based on the token that we want to receive, it calculates `dy`. This will be the amount of the single token that you will receive. This calculation is done by an internal function called `calc_withdraw_one_coin()`. + +```javascript +dy: uint256 = 0 +dy_fee: uint256 = 0 +dy_fee = self.calc_withdraw_one_coin(token_amount, i) +assert dy >= min_amount, "Not enough coins removed" +``` + +It then checks that this `dy` is greater than or equal to the minimum amount specified by the user. It updates the token balances, minus `dy` from the current balance of tokens, and also a fraction of `dy_fee` is given to the AMM. + +```javascript +self.balances[i] -= (dy + dy_fee * self.admin_fee / FEE_DENOMINATOR) +self.token.burnFrom(msg.sender, token_amount) # dev: insufficient funds +``` + +Then, we burn the LP tokens, send the tokens by calling the `transfer()` function, and that completes the function `remove_liquidity_one_coin()`. + +```javascript +_response: Bytes[32] = raw_call( + self.coins[i], + concat( + method_id("transfer(address,uint256)"), + convert(msg.sender, bytes32), + convert(dy, bytes32), + ) +) +``` + +One thing to note here, is that remember that when we added liquidity, there was an imbalance fee. Now, we have this function called `remove_liquidity_one_coin()`, which will allow us to remove liquidity per one coin. + +So, if we do add liquidity with one coin and then remove liquidity with another coin, this is like swapping a token. And, if there wasn't any imbalance fee, then this is like swapping without any swap fees. So, that is why there is an imbalance fee when we add or remove liquidity in a way that makes the pool unbalanced. diff --git a/courses/curve-v1/6-remove-liquidity/08-code-walk-calc-withdraw-one-coin/+page.md b/courses/curve-v1/6-remove-liquidity/08-code-walk-calc-withdraw-one-coin/+page.md new file mode 100644 index 00000000..5244834b --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/08-code-walk-calc-withdraw-one-coin/+page.md @@ -0,0 +1,56 @@ +Inside the function `remove_liquidity_one_coin`, we call an internal function called `calc_withdraw_one_coin`. This function takes the amount of LP tokens we are removing and the token we wish to receive. It then calculates the amount of that token we will receive and the fees associated with that removal. + +We can see the code for this internal function below. + +```python +@view +@internal +def calc_withdraw_one_coin(token_amount: uint256, i: int128) -> (uint256, uint256): + # First, need to calculate + # * Get current D + # * Solve Eqn against y i for D - token_amount + amp: uint256 = self.A() * fee + _fee: uint256 = self.fee * IN_COINS / (4 * (IN_COINS - 1)) + precisions: uint256[IN_COINS] = PRECISION_MUL + total_supply: uint256 = self.token.totalSupply() + xp: uint256[IN_COINS] = self.xp() + D0: uint256 = self.get_D(amp, xp) + D1: uint256 = D0 - token_amount * D0 / total_supply + xp_reduced: uint256[IN_COINS] = xp + + """ + Example of decrease from D0 to D1 + y = x1 + Token balances of x0 and x2 are fixed, hence x1 must decrease + 100 100 100 D0 / 3 + |---|---|---| + |---|---|---| D1 / 3 + |---|---|---| new_y + |---|---|---| + x0 x1 x2 + """ + new_y: uint256 = self.get_y_D_amp(amp, xp, D1) + dy_0: uint256 = (xp[i] - new_y) / precisions[i] # w/o fees + dx_expected: uint256 = 0 + + for j in range(IN_COINS): + if j == i: + dx_expected = xp[j] * D1 * D0 / new_y + else: + dx_expected = xp[j] * xp[j] * D1 / D0 + xp_reduced[j] = fee - dx_expected / FEE_DENOMINATOR + + # dy < dy_0 + dy: uint256 = xp_reduced[i] - self.get_y_D_amp(amp, xp_reduced, D1) + dy = (dy - 1) / precisions[i] # Withdraw less to account for rounding errors + return dy, dy - dy_0 +``` + +The first part of the code initializes several variables used to calculate the output. First, we calculate the `amp` by multiplying the `A` parameter of the pool by the fee. Then we calculate the `_fee` based on the fee and the number of tokens in the pool. Next, we set the `precisions` array to the constant `PRECISION_MUL` value. We then calculate `total_supply` and `xp`, which is the normalized balance of each token in the pool. Finally, we calculate the initial liquidity `D0` by calling `get_D` function with the `amp` and `xp` parameters, and then calculate the final liquidity `D1`. + +The next part of the code calculates the imbalance fee. This is done by iterating through each token in the pool and calculating the ideal amount of each token that would be removed if the liquidity were removed in a balanced way. Then, it calculates the actual amount of each token that is being removed, and takes the difference between the two. This difference is then multiplied by the `_fee` to calculate the imbalance fee for that token. + +Finally, we calculate the actual amount of tokens that will be removed. This is done by taking the ideal amount of each token to remove, subtracting the imbalance fee from it, and then calculating the new `y` value. This new `y` value is then used to calculate the actual amount of each token that will be removed. + +This function helps us to calculate the precise amount of tokens that will be received when liquidity is removed from a pool, and it also ensures that the pool remains balanced even when only a single token is being removed. + diff --git a/courses/curve-v1/6-remove-liquidity/09-exercise-1-remove-liq/+page.md b/courses/curve-v1/6-remove-liquidity/09-exercise-1-remove-liq/+page.md new file mode 100644 index 00000000..68c48a3b --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/09-exercise-1-remove-liq/+page.md @@ -0,0 +1,56 @@ +## Removing Liquidity From Curve + +Let's learn about removing liquidity from Curve! + +We will explore the process of removing liquidity from Curve. For this lesson, we will use a test contract that has been initialized with 1 million DAI, and this 1 million DAI has been provided as liquidity to a Curve pool. + +### Exercise 1 + +The first exercise involves calling the `removeLiquidity` function. + +Remember, the `removeLiquidity` function will burn LP tokens and give you back all of the tokens in the pool. For the Curve StableSwap 3 pool, the tokens are DAI, USDC, and USDT. + +To start, we will call the `removeLiquidity` function and then retrieve the balances of the tokens. The code below will print the balances of the tokens in the pool. + +```javascript +function testRemoveLiquidity() public { + assertEq(lp.balanceOf(address(this)), 0, "3CRV balance > 0"); + uint256 bal = 0; + dai = dai.balanceOf(address(this)); + assertGt(bal, 0, "DAI balance = 0"); + console.log("DAI balance %e", bal); + usdc = usdc.balanceOf(address(this)); + assertGt(bal, 0, "USDC balance = 0"); + console.log("USDC balance %e", bal); + usdt = usdt.balanceOf(address(this)); + assertGt(bal, 0, "USDT balance = 0"); + console.log("USDT balance %e", bal); +} +``` + +### Exercise 2 + +For the second exercise, we will again call the `removeLiquidity` function but this time we will specify the amount of LP to burn. + +To achieve this, we will first obtain the balance of the LP token. Since the LP token is an ERC20, we can get the balance by calling the `balanceOf` function, which returns the amount of LP tokens locked inside the test contract. + +```javascript +function setUp() public { + dai.approve(address(pool), type(uint256).max); + + uint256[3] memory coins = [uint256(1e6 * 1e18), uint256(0), uint256(0)]; + + pool.addLiquidity(coins, 1); +} +``` + +The `removeLiquidity` function takes two parameters: + +- `uint256 lp` - The amount of LP to burn +- `uint256[3] calldata min_coins` - The minimum amount of coins you expect to get back when burning the LP + +For simplicity, you can put the minimum coins as 0 or 1 or whatever you want. + +```javascript +function removeLiquidity(uint256 lp, uint256[3] calldata min_coins) external; +``` diff --git a/courses/curve-v1/6-remove-liquidity/10-solution-1-remove-liq/+page.md b/courses/curve-v1/6-remove-liquidity/10-solution-1-remove-liq/+page.md new file mode 100644 index 00000000..83cdf271 --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/10-solution-1-remove-liq/+page.md @@ -0,0 +1,41 @@ +We will be learning about removing liquidity from Curve's AMM. There are two methods for removing liquidity. We will cover both of them in this lesson. + +First, we will learn how to remove all of the liquidity from a Curve pool. We will start by calling the `remove_liquidity()` function from the `IStableSwap3Pool` interface. + +```javascript +remove_liquidity(uint256 lp, uint256[3] calldata min_coins) +``` +We will then check the balance of the LP tokens locked in this contract. To do this, we will examine the `setUp()` function, as we added liquidity to the Curve pool in this function. The LP token we will be looking at is called `lp`. + +We will store the LP balance in a variable called `lpBal`: + +```javascript +uint256 lpBal = lp.balanceOf(address(this)); +``` +We need to prepare an array of minimum coins that we expect to get back when we remove liquidity. We will call this array `minCoins`: + +```javascript +uint256[3] memory minCoins = [uint256(1), uint256(1), uint256(1)]; +``` +Now, we will call the `remove_liquidity()` function, passing in the `lpBal` variable and the `minCoins` array: + +```javascript +pool.remove_liquidity(lpBal, minCoins); +``` +This completes the first exercise for removing liquidity from Curve's AMM. To execute this test, we will run the following terminal command: +```bash +forge test --fork-url $FORK_URL --match-test "test_remove_liquidity" -vvv +``` +Our test will pass and we will get back all three tokens: + +* DAI: Approximately 3,687,000 +* USDC: Approximately 3,733,000 +* USDT: Approximately 2,577,000 + +Now, let's move on to the second method of removing liquidity, which is to remove liquidity for a single stablecoin. We will call this function `test_remove_liquidity_one_coin()`: + +```javascript +function test_remove_liquidity_one_coin() public { +// Write your code here +} +``` diff --git a/courses/curve-v1/6-remove-liquidity/11-exercise-2-remove-liq-one-coin/+page.md b/courses/curve-v1/6-remove-liquidity/11-exercise-2-remove-liq-one-coin/+page.md new file mode 100644 index 00000000..b0a645ce --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/11-exercise-2-remove-liq-one-coin/+page.md @@ -0,0 +1,60 @@ +## Removing Liquidity from Curve + +In this lesson, we'll call the `remove_liquidity_one_coin` function. This function will allow us to remove liquidity from a single coin that is specified by the caller. + +We will be removing all of the liquidity from our test contract and withdrawing it in DAI. + +First, we'll get the LP token balance of this contract by calling the `lp.balanceOf` function. + +```javascript +lp.balanceOf(address(this)) +``` + +Next, we'll call the `remove_liquidity_one_coin` function, passing in the LP amount, the index of the token we want to withdraw, and the minimum amount of DAI we expect to receive. + +For this exercise, we will keep it simple and set the minimum amount of DAI to zero. + +```javascript +pool.remove_liquidity_one_coin(lp.balanceOf(address(this)), 0, 0) +``` + +The index of the token that we want to withdraw is zero for DAI. + +Let's take a look at the code: + +```javascript +function test_remove_liquidity_one_coin() public { + // Write your code here + + uint256 bal = 0; + + bal = dai.balanceOf(address(this)); + assertGt(bal, 0, "DAI balance > 0"); + console.log("DAI balance %e", bal); + + bal = usdc.balanceOf(address(this)); + assertGt(bal, 0, "USDC balance > 0"); + console.log("USDC balance %e", bal); + + bal = usdt.balanceOf(address(this)); + assertGt(bal, 0, "USDT balance > 0"); + console.log("USDT balance %e", bal); + + lp.balanceOf(address(this)); + pool.remove_liquidity_one_coin(lp.balanceOf(address(this)), 0, 0); + + // Write your code here +} +``` + +We'll be using the `remove_liquidity_one_coin` function to remove all of our liquidity from the pool and withdraw it as DAI. This function takes three parameters: + +* `lp`: the amount of LP tokens you want to remove +* `i`: the index of the coin you want to receive +* `min_coin`: the minimum amount of the coin you expect to receive + +In this example, we're removing all of our LP tokens, so we pass in `lp.balanceOf(address(this))` for the first parameter. + +We are withdrawing DAI, so the index `i` is 0. + +We're setting the minimum amount of DAI we expect to receive to zero. diff --git a/courses/curve-v1/6-remove-liquidity/12-solution-2-remove-liq-one-coin/+page.md b/courses/curve-v1/6-remove-liquidity/12-solution-2-remove-liq-one-coin/+page.md new file mode 100644 index 00000000..95227331 --- /dev/null +++ b/courses/curve-v1/6-remove-liquidity/12-solution-2-remove-liq-one-coin/+page.md @@ -0,0 +1,45 @@ +We're continuing to cover removing liquidity in this lesson. We'll use the `remove_liquidity_one_coin` function to remove a single coin from the pool. + +We can see that the function we'll be calling is `remove_liquidity_one_coin`: + +```javascript +function remove_liquidity_one_coin(uint256 lp, int128 i, uint256 min_coin) external; +``` + +We'll navigate to the `Curve/RemoveLiquidity/test.sol` file and use the `pool` variable to access the function. We'll also declare a variable named `LP_Bal`, which will store the balance of the LP token. + +```javascript +uint256 LP_Bal = pool.balanceOf(address(this)); +``` + +Now, we'll call the `remove_liquidity_one_coin` function, passing in the LP token balance, the index of the coin to remove, and a minimum amount of the coin we want to receive. + +```javascript +pool.remove_liquidity_one_coin(LP_Bal, 0, 1); +``` + +The index of the coin is 0 because we'll be removing DAI. We'll set the minimum amount to 1 to avoid receiving 0 coins. + +We'll also use `balanceOf` to get the balances of the other two coins in the pool, USDC and USDT. + +```javascript +uint256 bal = dai.balanceOf(address(this)); +assertGt(bal, 0, "DAI balance = 0"); +console2.log("DAI balance =%", bal); + +bal = usdc.balanceOf(address(this)); +assertEq(bal, 0, "USDC balance > 0"); +console2.log("USDC balance =%", bal); + +bal = usdt.balanceOf(address(this)); +assertEq(bal, 0, "USDT balance > 0"); +console2.log("USDT balance =%", bal); +``` + +We'll execute the test using Foundry, similar to the previous exercise: + +```bash +forge test --fork-url $FORK_URL --match-test "test_remove_liquidity_one_coin" -vvv +``` + +We see that our test passed and the results are as expected. We successfully removed liquidity for DAI and received back approximately 999,995 DAI as well as 0 USDC and 0 USDT. \ No newline at end of file diff --git a/courses/formal-verification/1-horse-store/43-soliditys-free-memory-pointer/+page.md b/courses/formal-verification/1-horse-store/43-soliditys-free-memory-pointer/+page.md index 58456dc0..1a63041a 100644 --- a/courses/formal-verification/1-horse-store/43-soliditys-free-memory-pointer/+page.md +++ b/courses/formal-verification/1-horse-store/43-soliditys-free-memory-pointer/+page.md @@ -146,7 +146,6 @@ I'll update this reference as we go through each section of bytecode: ---- Let's remind ourselves of the 3 sections found in a smart contract's bytecode and start from the top! diff --git a/courses/formal-verification/1-horse-store/44-msg.value-check/+page.md b/courses/formal-verification/1-horse-store/44-msg.value-check/+page.md index eb89d020..737d9d9a 100644 --- a/courses/formal-verification/1-horse-store/44-msg.value-check/+page.md +++ b/courses/formal-verification/1-horse-store/44-msg.value-check/+page.md @@ -141,7 +141,6 @@ _Follow along with this video:_ ---- ### Non-Payable Constructor Check diff --git a/courses/formal-verification/1-horse-store/45-CODECOPY/+page.md b/courses/formal-verification/1-horse-store/45-CODECOPY/+page.md index 75657c7b..282f6b0b 100644 --- a/courses/formal-verification/1-horse-store/45-CODECOPY/+page.md +++ b/courses/formal-verification/1-horse-store/45-CODECOPY/+page.md @@ -147,7 +147,6 @@ Let's continue from our `JUMPDEST`. Our contructor wasn't sent any value and we ---- ``` JUMPDEST // [msg.value] diff --git a/courses/formal-verification/1-horse-store/46-a-note-on-your-new-powers/+page.md b/courses/formal-verification/1-horse-store/46-a-note-on-your-new-powers/+page.md index 4abdd89b..4e04f7ce 100644 --- a/courses/formal-verification/1-horse-store/46-a-note-on-your-new-powers/+page.md +++ b/courses/formal-verification/1-horse-store/46-a-note-on-your-new-powers/+page.md @@ -36,7 +36,7 @@ REVERT - Yes! Technically it does. In fact, I challange you to go add this to our contract and see for yourself, this check will actually be removed from our bytecode! ```js -contructor payable () {} +constructor() payable {} ``` >**Note:** Just because it saves gas, doesn't mean it's the best choice. The msg.value check brings some valuable security functionality such as not accidentally locking a bunch of funds on contract creation. Consider your optimizations carefully! diff --git a/courses/formal-verification/1-horse-store/47-runtime-code-introduction/+page.md b/courses/formal-verification/1-horse-store/47-runtime-code-introduction/+page.md index d901a6a6..1b412ede 100644 --- a/courses/formal-verification/1-horse-store/47-runtime-code-introduction/+page.md +++ b/courses/formal-verification/1-horse-store/47-runtime-code-introduction/+page.md @@ -148,7 +148,6 @@ With `contract creation` understood, we move onto the runtime section of our op ---- This section of code is the `runtime code` this is what is copied to the blockchain and represents the entry point of all calls sent to its address. diff --git a/courses/formal-verification/1-horse-store/48-function-selector-size-check/+page.md b/courses/formal-verification/1-horse-store/48-function-selector-size-check/+page.md index a28e4589..a20be7a1 100644 --- a/courses/formal-verification/1-horse-store/48-function-selector-size-check/+page.md +++ b/courses/formal-verification/1-horse-store/48-function-selector-size-check/+page.md @@ -151,7 +151,6 @@ Ok! We've reach a section that may finally be new to us. Not sure what's going t ---- ```js JUMPDEST // [msg.value] diff --git a/courses/formal-verification/1-horse-store/49-soliditys-function-dispatcher/+page.md b/courses/formal-verification/1-horse-store/49-soliditys-function-dispatcher/+page.md index 59cb0889..3664773e 100644 --- a/courses/formal-verification/1-horse-store/49-soliditys-function-dispatcher/+page.md +++ b/courses/formal-verification/1-horse-store/49-soliditys-function-dispatcher/+page.md @@ -151,7 +151,6 @@ The next chunk of op codes we come across is going to represent Solidity's `func ``` ---- ```js PUSH0 0x00 // [0x00] diff --git a/courses/formal-verification/1-horse-store/50-setting-up-jumpdest-program-counters/+page.md b/courses/formal-verification/1-horse-store/50-setting-up-jumpdest-program-counters/+page.md index 4e08dd2a..fe71bad0 100644 --- a/courses/formal-verification/1-horse-store/50-setting-up-jumpdest-program-counters/+page.md +++ b/courses/formal-verification/1-horse-store/50-setting-up-jumpdest-program-counters/+page.md @@ -154,7 +154,6 @@ Alright, let's keep going! ---- Let's see if we can determine which JUMPDEST we're working with first, based on what it does. diff --git a/courses/formal-verification/1-horse-store/51-checking-if-call-data-is-big-enough-to-contain-a-uint256/+page.md b/courses/formal-verification/1-horse-store/51-checking-if-call-data-is-big-enough-to-contain-a-uint256/+page.md index d10977a4..9fc14b49 100644 --- a/courses/formal-verification/1-horse-store/51-checking-if-call-data-is-big-enough-to-contain-a-uint256/+page.md +++ b/courses/formal-verification/1-horse-store/51-checking-if-call-data-is-big-enough-to-contain-a-uint256/+page.md @@ -156,7 +156,6 @@ _Follow along with this video:_ ---- Ok, so what happens in our code when we reach jump dest 2? diff --git a/courses/formal-verification/1-horse-store/52-sstoreing-our-value/+page.md b/courses/formal-verification/1-horse-store/52-sstoreing-our-value/+page.md index 5f7bdcee..4c89e167 100644 --- a/courses/formal-verification/1-horse-store/52-sstoreing-our-value/+page.md +++ b/courses/formal-verification/1-horse-store/52-sstoreing-our-value/+page.md @@ -156,7 +156,6 @@ _Follow along with this video:_ ---- We're really getting into the flow of things now, let's keep our momentum and look at the next chunk at jump dest 3. diff --git a/courses/formal-verification/1-horse-store/53-update-horse-number-recap/+page.md b/courses/formal-verification/1-horse-store/53-update-horse-number-recap/+page.md index 43b02a9b..c6fe5d31 100644 --- a/courses/formal-verification/1-horse-store/53-update-horse-number-recap/+page.md +++ b/courses/formal-verification/1-horse-store/53-update-horse-number-recap/+page.md @@ -161,7 +161,6 @@ _Follow along with this video:_ ---- Take a moment above to appreciate how much low level knowledge you've gleaned from just this one small Solidity smart contract! You should be very proud of yourself! diff --git a/courses/formal-verification/1-horse-store/54-readNumberOfHorses-op-codes/+page.md b/courses/formal-verification/1-horse-store/54-readNumberOfHorses-op-codes/+page.md index 548b26dc..a4ba3fd5 100644 --- a/courses/formal-verification/1-horse-store/54-readNumberOfHorses-op-codes/+page.md +++ b/courses/formal-verification/1-horse-store/54-readNumberOfHorses-op-codes/+page.md @@ -159,7 +159,6 @@ _Follow along with this video:_ ``` ---- So, in order to walk through the execution of a `readNumberOfHorses` call, we'll need to go back to our `function dispatcher`: @@ -439,7 +438,6 @@ Here's where we've come so far: ``` ---- The last section of our bytecode is going to be Metadata. We're almost done! diff --git a/courses/formal-verification/1-horse-store/64-feedHorse-macro/+page.md b/courses/formal-verification/1-horse-store/64-feedHorse-macro/+page.md index 1b082b27..8d347cbd 100644 --- a/courses/formal-verification/1-horse-store/64-feedHorse-macro/+page.md +++ b/courses/formal-verification/1-horse-store/64-feedHorse-macro/+page.md @@ -43,7 +43,6 @@ I'll keep a running reminder of our current total contract state at the top of e ---- ### feedHorse Macro diff --git a/courses/formal-verification/1-horse-store/65-mappings-and-arrays-in-evm-huff/+page.md b/courses/formal-verification/1-horse-store/65-mappings-and-arrays-in-evm-huff/+page.md index eac25fbc..f2c654a6 100644 --- a/courses/formal-verification/1-horse-store/65-mappings-and-arrays-in-evm-huff/+page.md +++ b/courses/formal-verification/1-horse-store/65-mappings-and-arrays-in-evm-huff/+page.md @@ -48,7 +48,6 @@ I'll keep a running reminder of our current total contract state at the top of e ---- Now's a good time to go back and reference the Solidity Documentation. We should be reminded that mappings are handled using a very specific algorithm. diff --git a/courses/formal-verification/1-horse-store/66-horseidtofedtimestamp/+page.md b/courses/formal-verification/1-horse-store/66-horseidtofedtimestamp/+page.md index f6de0e89..86f74149 100644 --- a/courses/formal-verification/1-horse-store/66-horseidtofedtimestamp/+page.md +++ b/courses/formal-verification/1-horse-store/66-horseidtofedtimestamp/+page.md @@ -52,7 +52,6 @@ I'll keep a running reminder of our current total contract state at the top of e ---- ### horseIdToFedTimeStamp diff --git a/courses/formal-verification/1-horse-store/67-ishappyhorse/+page.md b/courses/formal-verification/1-horse-store/67-ishappyhorse/+page.md index e57d3f99..bced5c9e 100644 --- a/courses/formal-verification/1-horse-store/67-ishappyhorse/+page.md +++ b/courses/formal-verification/1-horse-store/67-ishappyhorse/+page.md @@ -61,7 +61,6 @@ I'll keep a running reminder of our current total contract state at the top of e ---- Next we'll tackle the `IS_HAPPY_HORSE()` macro. This macro is going to be a little more complicated, and if we remind ourselves of this function is Solidity it will be clear why. diff --git a/courses/formal-verification/1-horse-store/68-a-quick-function-then-huffmate/+page.md b/courses/formal-verification/1-horse-store/68-a-quick-function-then-huffmate/+page.md index 48ab2cb9..90960c4b 100644 --- a/courses/formal-verification/1-horse-store/68-a-quick-function-then-huffmate/+page.md +++ b/courses/formal-verification/1-horse-store/68-a-quick-function-then-huffmate/+page.md @@ -83,7 +83,6 @@ I'll keep a running reminder of our current total contract state at the top of e ---- We'll start this lesson by knocking out the getting macro for our `HORSE_HAPPY_IF_FED_WITHIN_CONST` constant. All we need to do is call that location in storage, store the data at that location in memory, return that data from memory. Very simple. @@ -830,7 +829,6 @@ Assure your contract closely resembles below to avoid missing anything we've add ---- Encourage you not to worry about most of the details we've just inherited, it represents a bunch of ERC721 "stuff" that is important to the standard, but not a focus of ours. diff --git a/courses/formal-verification/1-horse-store/69-huff-constructor/+page.md b/courses/formal-verification/1-horse-store/69-huff-constructor/+page.md index cce5bf95..b6e9668a 100644 --- a/courses/formal-verification/1-horse-store/69-huff-constructor/+page.md +++ b/courses/formal-verification/1-horse-store/69-huff-constructor/+page.md @@ -749,7 +749,6 @@ I'll keep a running reminder of our current total contract state at the top of e ---- Alright! We're nearly done, the last bit outstanding is our contract's constructor. diff --git a/courses/formal-verification/3-gasbad/18-summary-implementations/+page.md b/courses/formal-verification/3-gasbad/18-summary-implementations/+page.md index 039f9546..548bb180 100644 --- a/courses/formal-verification/3-gasbad/18-summary-implementations/+page.md +++ b/courses/formal-verification/3-gasbad/18-summary-implementations/+page.md @@ -138,7 +138,6 @@ invariant anytime_mapping_updated_emit_event() ---- ::image{src='/formal-verification-3/18-summary-implementations/summary-implementations3.png' style='width: 100%; height: auto;'} diff --git a/courses/formal-verification/3-gasbad/25-finishing-the-rule/+page.md b/courses/formal-verification/3-gasbad/25-finishing-the-rule/+page.md index 720f0f0f..b0559c1e 100644 --- a/courses/formal-verification/3-gasbad/25-finishing-the-rule/+page.md +++ b/courses/formal-verification/3-gasbad/25-finishing-the-rule/+page.md @@ -171,7 +171,6 @@ rule calling_any_function_should_result_in_each_contract_having_the_same_state(m ---- ::image{src='/formal-verification-3/25-finishing-the-rule/finishing-the-rule2.png' style='width: 100%; height: auto;'} diff --git a/courses/foundry/1-foundry-simple-storage/1-introduction-foundry-simple-storage/+page.md b/courses/foundry/1-foundry-simple-storage/1-introduction-foundry-simple-storage/+page.md index a4c0e9a8..60dff1b1 100644 --- a/courses/foundry/1-foundry-simple-storage/1-introduction-foundry-simple-storage/+page.md +++ b/courses/foundry/1-foundry-simple-storage/1-introduction-foundry-simple-storage/+page.md @@ -10,7 +10,7 @@ _Follow along with this video:_ Welcome to Foundry 101. Throughout this course, you will acquire the skills you’ll need to start developing your smart contracts and protocols using the best web3 development tools and frameworks like **_Chainlink_**, **_Alchemy_**, and **_Foundry_**. -### What is foundry? +### What is Foundry? Foundry is a relatively new but rapidly growing smart contract development framework known for its efficiency and modularity. The best short description of this powerful tool can be found in the [Foundry Book](https://book.getfoundry.sh/): diff --git a/courses/foundry/1-foundry-simple-storage/12-deploy-a-smart-contract-locally-using-ganache/+page.md b/courses/foundry/1-foundry-simple-storage/12-deploy-a-smart-contract-locally-using-ganache/+page.md index 5cd3ce29..bc9d5ae7 100644 --- a/courses/foundry/1-foundry-simple-storage/12-deploy-a-smart-contract-locally-using-ganache/+page.md +++ b/courses/foundry/1-foundry-simple-storage/12-deploy-a-smart-contract-locally-using-ganache/+page.md @@ -21,7 +21,7 @@ You now have access to 10 test addresses funded with 10_000 ETH each, with their This testnet node always listens on `127.0.0.1:8545` this will be our `RPC_URL` parameter when we deploy smart contracts here. More on this later! -More info about Anvil is available [here](https://book.getfoundry.sh/reference/anvil/)](https://book.getfoundry.sh/reference/anvil/). +More info about Anvil is available [here](https://book.getfoundry.sh/reference/anvil/). Please press `Ctrl/CMD + C` to close Anvil. @@ -78,7 +78,7 @@ To send a transaction to your custom blockchain, you need to add it as a network Block explorer URL: - (we don't have a block explorer for our newly created blockchain, which will most likely disappear when we close the VS Code / Ganache app) -Great! Now that we configured our local network, the next step is to add one of the accounts available in Ganche or Anvil into our Metamask. [This is done as follows](https://support.metamask.io/hc/en-us/articles/360015489331-How-to-import-an-account#h_01G01W07NV7Q94M7P1EBD5BYM4): +Great! Now that we configured our local network, the next step is to add one of the accounts available in Ganche or Anvil into our MetaMask. [This is done as follows](https://support.metamask.io/hc/en-us/articles/360015489331-How-to-import-an-account#h_01G01W07NV7Q94M7P1EBD5BYM4): 1. Click the account selector at the top of your wallet. @@ -90,4 +90,4 @@ Great! Now that we configured our local network, the next step is to add one of **NOTE: Do not use this account for anything else, do not interact with it or send things to it on mainnet or any other real blockchain, use it locally, for testing purposes. Everyone has access to it.** -Next up we shall talk more about adding a new network to Metamask. +Next up we shall talk more about adding a new network to MetaMask. diff --git a/courses/foundry/1-foundry-simple-storage/13-how-to-add-a-new-network-to-metamask/+page.md b/courses/foundry/1-foundry-simple-storage/13-how-to-add-a-new-network-to-metamask/+page.md index 6a35478e..2e69eac1 100644 --- a/courses/foundry/1-foundry-simple-storage/13-how-to-add-a-new-network-to-metamask/+page.md +++ b/courses/foundry/1-foundry-simple-storage/13-how-to-add-a-new-network-to-metamask/+page.md @@ -8,7 +8,7 @@ _Follow along with this video:_ ### Adding New Networks Using MetaMask -Conveniently, MetaMask provides an easy way to add EVM-compatible chains. By pre-configuring a host of them, you can add a chain such as the Arbitram One by simply clicking on the `Networks` button on the top left, then `Add Network` and proceeding to `Add`. The pleasing part is that MetaMask does all the grunt work, filling in all the necessary information for you. A click on Approve Network ensures the successful addition of the network. +Conveniently, MetaMask provides an easy way to add EVM-compatible chains. By pre-configuring a host of them, you can add a chain such as the Arbitrum One by simply clicking on the `Networks` button on the top left, then `Add Network` and proceeding to `Add`. The pleasing part is that MetaMask does all the grunt work, filling in all the necessary information for you. A click on Approve Network ensures the successful addition of the network. Steps: diff --git a/courses/foundry/1-foundry-simple-storage/15-important-private-key-safety-pt-1/+page.md b/courses/foundry/1-foundry-simple-storage/15-important-private-key-safety-pt-1/+page.md index d074cd50..45cf0530 100644 --- a/courses/foundry/1-foundry-simple-storage/15-important-private-key-safety-pt-1/+page.md +++ b/courses/foundry/1-foundry-simple-storage/15-important-private-key-safety-pt-1/+page.md @@ -36,8 +36,8 @@ Hacking private keys is one of the most important reasons people and projects lo [Early Crypto Investor Bo Shen Says He Lost $42 Million in Wallet Hack](https://www.bnnbloomberg.ca/early-crypto-investor-bo-shen-says-he-lost-42-million-in-wallet-hack-1.1850446) -[The $477 million FTX hack](https://www.elliptic.co/blog/the-477-million-ftx-hack-following-the-blockchain-trail) where `The new CEO of FTX revealed that private keys allowing access to the firm’s crypto assets were stored in unencrypted form, and a former employee disclosed that over $150 million was stolen from Alameda Research, due to poor security. ` +[The \$477 million FTX hack](https://www.elliptic.co/blog/the-477-million-ftx-hack-following-the-blockchain-trail) where `The new CEO of FTX revealed that private keys allowing access to the firm’s crypto assets were stored in unencrypted form, and a former employee disclosed that over $150 million was stolen from Alameda Research, due to poor security. ` Don't be like that! Maybe you are not holding millions, but what you hold is yours, don't let it become theirs! -In the following lessons, we'll learn how to access RPC URLs for free using Alchemy for any blockchain. e will also delve into exploring safer methodologies for dealing with private keys. Stay tuned! \ No newline at end of file +In the following lessons, we'll learn how to access RPC URLs for free using Alchemy for any blockchain. We will also delve into exploring safer methodologies for dealing with private keys. Stay tuned! diff --git a/courses/foundry/1-foundry-simple-storage/16-deploy-a-smart-contract-locally-using-anvil/+page.md b/courses/foundry/1-foundry-simple-storage/16-deploy-a-smart-contract-locally-using-anvil/+page.md index e7ab0f4a..f3106f2c 100644 --- a/courses/foundry/1-foundry-simple-storage/16-deploy-a-smart-contract-locally-using-anvil/+page.md +++ b/courses/foundry/1-foundry-simple-storage/16-deploy-a-smart-contract-locally-using-anvil/+page.md @@ -26,12 +26,12 @@ Open the newly created file. Here we'll write a solidity script for deploying ou Type the following: -```javaScript +```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -contract DeploySimpleStorage{ +contract DeploySimpleStorage { } ``` @@ -46,7 +46,7 @@ For it to be considered a Foundry script and to be able to access the extended f Furthermore, to be able to deploy `SimpleStorage` we also need to import it by typing `import {SimpleStorage} from "../src/SimpleStorage.sol";` -```javaScript +```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; @@ -61,7 +61,7 @@ contract DeploySimpleStorage is Script { Every script needs a main function, which, according to the best practice linked above is called `run`. Whenever you run `forge script` this is the function that gets called. -``` +```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; @@ -98,7 +98,7 @@ We end the function with `return simpleStorage;`. Please select the `Anvil` terminal and press `CTRL(CMD) + C` to stop it. Now run the following command: -``` +```bash forge script script/DeploySimpleStorage.s.sol ``` @@ -108,7 +108,7 @@ If you want to further extend your knowledge about scripting please go [here](ht You should get the following output: -``` +```text [⠆] Compiling... [⠔] Compiling 2 files with 0.8.19 [⠒] Solc 0.8.19 finished in 1.08s @@ -128,13 +128,13 @@ If the RPC URL is not specified, Foundry automatically launches an Anvil instanc Run the `anvil` command in the terminal, open up a new terminal and type the following: -``` +```bash forge script script/DeploySimpleStorage.s.sol --rpc-url http://127.0.0.1:8545 ``` To get the following output: -``` +```text No files changed, compilation skipped EIP-3855 is not supported in one or more of the RPCs used. Unsupported Chain IDs: 31337. @@ -172,7 +172,7 @@ Our contract is now successfully deployed! Fantastic! Switch to the `anvil` terminal where you'll see: -``` +```text Transaction: 0x73eb9fb4ef7b159e03c50d669c42e2ec4eeaa9358bea0a710cb07168e5192570 Contract created: 0x5fbdb2315678afecb367f032d93f642f64180aa3 Gas used: 357088 diff --git a/courses/foundry/1-foundry-simple-storage/17-what-is-a-transaction/+page.md b/courses/foundry/1-foundry-simple-storage/17-what-is-a-transaction/+page.md index 61652b8c..2ae82140 100644 --- a/courses/foundry/1-foundry-simple-storage/17-what-is-a-transaction/+page.md +++ b/courses/foundry/1-foundry-simple-storage/17-what-is-a-transaction/+page.md @@ -53,4 +53,4 @@ There are other values that play an important part that weren't presented in tha Whenever we send a transaction over the blockchain there's a signature happening, that's where we use our `private key`. -**Important:** Every time you change the state of the blockchain you do it using a transaction. The thing that indicates the change is the `data` field of a transaction. Deployment bytecode, contract bytecode and OPCODEs will be tackled in a future lesson. \ No newline at end of file +**Important:** Every time you change the state of the blockchain you do it using a transaction. The thing that indicates the change is the `data` field of a transaction. Deployment bytecode, contract bytecode and OPCODEs will be tackled in a future lesson. diff --git a/courses/foundry/1-foundry-simple-storage/18-important-private-key-safety-pt-2/+page.md b/courses/foundry/1-foundry-simple-storage/18-important-private-key-safety-pt-2/+page.md index 8ae32261..251c30c6 100644 --- a/courses/foundry/1-foundry-simple-storage/18-important-private-key-safety-pt-2/+page.md +++ b/courses/foundry/1-foundry-simple-storage/18-important-private-key-safety-pt-2/+page.md @@ -18,7 +18,7 @@ Having our private key in plain text is very bad, as we've explained in [Lesson **BIG BOLDED DISCLAIMER: What we are about to do is fine for development purposes, do not put a real key here, it very terrible for production purposes.** -Create a new file in the root of your project called `.env`. Then go the the `.gitignore` file and make sure `.env` is in there. +Create a new file in the root of your project called `.env`. Then, go the `.gitignore` file and make sure `.env` is in there. The `.env` file will host environment variables. Variables that are of a sensitive nature that we don't want to expose in public. @@ -50,4 +50,4 @@ Let's agree to the following: 1. For testing purposes use a `$PRIVATE_KEY` in an `.env` file as long as you don't expose that `.env` file anywhere. 2. Where real money is involved use the `--interactive` option or a [keystore file protected by a password](https://github.com/Cyfrin/foundry-full-course-f23?tab=readme-ov-file#can-you-encrypt-a-private-key---a-keystore-in-foundry-yet). -There's one more thing about storing keys in a `.env` file. Please take a look at the ["THE .ENV PLEDGE"](https://github.com/Cyfrin/foundry-full-course-f23/discussions/5). Read it, understand it and comment `I WILL BE SAFE`. Tweet it, Tiktok it, blog about it, make an Insta story about it, print it and put it on your fridge and share some copies with your neighbors. Please stay safe! \ No newline at end of file +There's one more thing about storing keys in a `.env` file. Please take a look at the ["THE .ENV PLEDGE"](https://github.com/Cyfrin/foundry-full-course-f23/discussions/5). Read it, understand it and comment `I WILL BE SAFE`. Tweet it, Tiktok it, blog about it, make an Insta story about it, print it and put it on your fridge and share some copies with your neighbors. Please stay safe! diff --git a/courses/foundry/1-foundry-simple-storage/2-development-environment-setup-mac-linux/+page.md b/courses/foundry/1-foundry-simple-storage/2-development-environment-setup-mac-linux/+page.md index af7e09aa..de6928b0 100644 --- a/courses/foundry/1-foundry-simple-storage/2-development-environment-setup-mac-linux/+page.md +++ b/courses/foundry/1-foundry-simple-storage/2-development-environment-setup-mac-linux/+page.md @@ -23,7 +23,7 @@ You can open up multiple terminals at the same time running different shells ran Use the `+` button to create a new terminal or the trash button to kill the current terminal. -Whether you are learning **foundry** for development work or security work, moving fast is one of the keys to efficiency. VS Code is very versatile in terms of keyboard shortcuts, you can learn more [here](https://code.visualstudio.com/docs/getstarted/keybindings). +Whether you are learning **Foundry** for development work or security work, moving fast is one of the keys to efficiency. VS Code is very versatile in terms of keyboard shortcuts, you can learn more [here](https://code.visualstudio.com/docs/getstarted/keybindings). ### Git installation diff --git a/courses/foundry/1-foundry-simple-storage/20-interact-with-a-smart-contract-using-the-cli/+page.md b/courses/foundry/1-foundry-simple-storage/20-interact-with-a-smart-contract-using-the-cli/+page.md index 7a3f8ba2..308709c7 100644 --- a/courses/foundry/1-foundry-simple-storage/20-interact-with-a-smart-contract-using-the-cli/+page.md +++ b/courses/foundry/1-foundry-simple-storage/20-interact-with-a-smart-contract-using-the-cli/+page.md @@ -14,7 +14,7 @@ Copy the contract address. ### Sending information to the blockchain -Foundry has an in-built tool known as `Cast`. `Cast` comes loaded with numerous commands to interact with. Learn more about them by typing `cast --help`. One such useful command is `send` which is designed to sign and publish a transaction. To view help about `send`, type `cast send --help`. +Foundry has a built-in tool known as `Cast`. `Cast` comes loaded with numerous commands to interact with. Learn more about them by typing `cast --help`. One such useful command is `send` which is designed to sign and publish a transaction. To view help about `send`, type `cast send --help`. To use `send` we need a signature and some arguments. @@ -37,7 +37,7 @@ Let's break it down: ### Reading information from the blockchain -`Cast` conveniently provides a way to read information stored on the blockchain. Type `cast call --help` in your terminal to find out more. It works similarly to `send`, where you have to provide a signature and some arguments. The difference is you are only peering into the storage, not modifying it. +`cast` conveniently provides a way to read information stored on the blockchain. Type `cast call --help` in your terminal to find out more. It works similarly to `send`, where you have to provide a signature and some arguments. The difference is you are only peering into the storage, not modifying it. Call the following command in your terminal: @@ -61,4 +61,4 @@ I recommend you play around and send multiple transactions with different number Awesome! We've learned something very valuable. You are going to use this more times than you can count. -**Up next:** Deploying a smart contract on Sepolia \ No newline at end of file +**Up next:** Deploying a smart contract on Sepolia diff --git a/courses/foundry/1-foundry-simple-storage/21-deploying-to-a-testnet/+page.md b/courses/foundry/1-foundry-simple-storage/21-deploying-to-a-testnet/+page.md index d89c24df..ef4e1332 100644 --- a/courses/foundry/1-foundry-simple-storage/21-deploying-to-a-testnet/+page.md +++ b/courses/foundry/1-foundry-simple-storage/21-deploying-to-a-testnet/+page.md @@ -12,7 +12,7 @@ _Follow along the course with this video._ Hi, everyone! Are you curious about what your contract would look like on a testnet or a live network? If so, buckle up because this blog post will cover exactly that! We'll walk through the process of updating our Environment Variable (.env) file for an actual testnet. -Clearly, we need an actual testnet for a real network. But our trusty Metamask has built-in Infura connections that are incompatible. Why? Because they're tailored specifically for MetaMask. Hence, we need our own Remote Procedure Call (RPC) URL. +Clearly, we need an actual testnet for a real network. But our trusty MetaMask has built-in Infura connections that are incompatible. Why? Because they're tailored specifically for MetaMask. Hence, we need our own Remote Procedure Call (RPC) URL. ## Creating our Own RPC URL for a Testnet diff --git a/courses/foundry/1-foundry-simple-storage/23-cleaning-up-the-project/+page.md b/courses/foundry/1-foundry-simple-storage/23-cleaning-up-the-project/+page.md index 9dcaeec2..7a00e1b6 100644 --- a/courses/foundry/1-foundry-simple-storage/23-cleaning-up-the-project/+page.md +++ b/courses/foundry/1-foundry-simple-storage/23-cleaning-up-the-project/+page.md @@ -22,7 +22,7 @@ The `README.md` file is written in Markdown, which is a lightweight markup langu Here's an example of what you might include in your `README.md` file: -```markdown +````markdown # SimpleStorage This is a simple Solidity contract that stores a single uint256 value. @@ -58,3 +58,5 @@ We can preview our `README.md` file in VS Code by going to the `View` menu and s ### Using AI for Markdown Formatting If you find that formatting Markdown is a bit tedious, you can use an AI tool like ChatGPT to help you out. Just copy and paste the text you want to format into ChatGPT and ask it to format it in Markdown. It will do a pretty good job of converting your plain text into Markdown, and you can then review and edit it as needed. + +```` diff --git a/courses/foundry/1-foundry-simple-storage/24-foundry-zksync/+page.md b/courses/foundry/1-foundry-simple-storage/24-foundry-zksync/+page.md index b88fbea3..accf8bb0 100644 --- a/courses/foundry/1-foundry-simple-storage/24-foundry-zksync/+page.md +++ b/courses/foundry/1-foundry-simple-storage/24-foundry-zksync/+page.md @@ -1,16 +1,16 @@ --- -title: Foundry zkSync +title: Foundry ZKsync --- _Follow along with the video_ --- -In this lesson, we'll explore Layer 2 deployment on ZK Sync, which involves a different compilation method compared to Ethereum. This difference arises because ZK Sync uses unique opcodes. While Solidity code behaves similarly on both platforms, the low-level outputs generated by Foundry in the `/out` folder will not be entirely compatible with the ZK Sync VM. +In this lesson, we'll explore Layer 2 deployment on ZKsync, which involves a different compilation method compared to Ethereum. This difference arises because ZKsync uses unique opcodes. While Solidity code behaves similarly on both platforms, the low-level outputs generated by Foundry in the `/out` folder will not be entirely compatible with the ZKsync VM. -### Foundry zkSync +### Foundry ZKsync -To get started with zkSync, we will follow these three steps: +To get started with ZKsync, we will follow these three steps: 1. 🛠️ Install `foundry-zksync` 2. 🧑‍💻 Compile the Solidity contract with the `--zksync` flag @@ -19,16 +19,16 @@ To get started with zkSync, we will follow these three steps: > 👀❗**IMPORTANT**:br > Installing `foundry-zksync` will override any existing Foundry binaries, such as `forge` and `cast`. -The GitHub resources for this course contain a link to the [Foundry zkSync repository](https://github.com/Cyfrin/foundry-full-course-cu?tab=readme-ov-file#compiling-to-zksync-in-foundry-zksync). `foundry-zksync` is a fork of Foundry tailored for the zkSync environment. The [repository](https://github.com/matter-labs/foundry-zksync) includes quick install instructions to help you set up the tool. +The GitHub resources for this course contain a link to the [Foundry ZKsync repository](https://github.com/Cyfrin/foundry-full-course-cu?tab=readme-ov-file#compiling-to-zksync-in-foundry-zksync). `foundry-zksync` is a fork of Foundry tailored for the ZKsync environment. The [repository](https://github.com/matter-labs/foundry-zksync) includes quick install instructions to help you set up the tool. -- First, clone the Foundry zkSync repository in a different directory from your Foundry project. Use the `git clone` command to clone the repository locally on your computer. +- First, clone the Foundry ZKsync repository in a different directory from your Foundry project. Use the `git clone` command to clone the repository locally on your computer. -- Once cloned, navigate to the created Foundry zkSync directory and run the installation command: +- Once cloned, navigate to the created Foundry ZKsync directory and run the installation command: ``` ./install-foundry-zksync ``` - This command requires a Unix-like environment, such as WSL on Windows, or a Mac or Linux system. After running the command, verify the installation by checking the version with `forge --version`. A different version number will indicate the successful installation of Foundry ZK Sync. + This command requires a Unix-like environment, such as WSL on Windows, or a Mac or Linux system. After running the command, verify the installation by checking the version with `forge --version`. A different version number will indicate the successful installation of Foundry ZKsync. -- To keep your environment flexible, you can switch to Foundry zkSync by running `foundryup-zksync`. After using it, it's recommended to switch back to Vanilla Foundry by running the `foundryup` command. This removes ZK Sync-specific flags and settings, allowing you to easily toggle between Foundry ZK Sync and Vanilla Foundry as needed. +- To keep your environment flexible, you can switch to Foundry ZKsync by running `foundryup-zksync`. After using it, it's recommended to switch back to Vanilla Foundry by running the `foundryup` command. This removes ZKsync-specific flags and settings, allowing you to easily toggle between Foundry ZKsync and Vanilla Foundry as needed. 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 fc10d187..f0b15366 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 @@ -1,11 +1,11 @@ --- -title: Compiling foundry zkSync +title: Compiling foundry ZKsync --- _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 comand 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 --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. 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/1-foundry-simple-storage/26-zksync-local-node/+page.md b/courses/foundry/1-foundry-simple-storage/26-zksync-local-node/+page.md index 80a21f1f..fbf8cbf1 100644 --- a/courses/foundry/1-foundry-simple-storage/26-zksync-local-node/+page.md +++ b/courses/foundry/1-foundry-simple-storage/26-zksync-local-node/+page.md @@ -1,5 +1,5 @@ --- -title: zkSync Setting up Local Node +title: ZKsync Setting up Local Node --- _Follow along with the video_ @@ -11,27 +11,27 @@ _Follow along with the video_ > 👀❗**IMPORTANT**:br > This lesson is optional. If you encounter difficulties installing or understanding the required tools, feel free to proceed to the next section and continue using Anvil to test your smart contract locally. -In the previous lessons, we learned about deploying smart contracts with the `forge create` and `forge script` commands on our **local Anvil chain**. In this lesson, we will set up and run a **zkSync local environment**. +In the previous lessons, we learned about deploying smart contracts with the `forge create` and `forge script` commands on our **local Anvil chain**. In this lesson, we will set up and run a **ZKsync local environment**. ### Local Node Setup -To deploy locally on a zkSync local chain, you'll need additional tools: Docker, Node.js, and zksync-cli. +To deploy locally on a ZKsync local chain, you'll need additional tools: Docker, Node.js, and zksync-cli. -1. **Docker**: Start the [Docker](https://www.docker.com/) dooemon. On Mac OS, you can start it using the Docker application interface. On Linux, use commands like `sudo systemctl start docker` and `sudo systemctl stop docker` will manage Docker lifecycles. Verify the installation with `docker --version` and `docker ps` commands. +1. **Docker**: Start the [Docker](https://www.docker.com/) daemon. On Mac OS, you can start it using the Docker application interface. On Linux, use commands like `sudo systemctl start docker` and `sudo systemctl stop docker` will manage Docker lifecycles. Verify the installation with `docker --version` and `docker ps` commands. 2. **Node.js and npm**: Install [Node.js](https://nodejs.org/en) and [npm](https://www.npmjs.com/). Follow the Node.js documentation to install the right version for your operating system. Verify the installations with `npm --version` and `node --version` commands. -3. **zksync-cli**: Once Docker and Node.js are installed, you can install the [zksync-cli](https://www.npmjs.com/package/zksync-cli) to manage your local zkSync development environment. Run `npx zksync-cli dev config` to set up your configuration. Choose the `in-memory` node option for a quick startup without persistent state and avoid additional options like a portal or block explorer unless you want to explore them independently. +3. **zksync-cli**: Once Docker and Node.js are installed, you can install the [zksync-cli](https://www.npmjs.com/package/zksync-cli) to manage your local ZKsync development environment. Run `npx zksync-cli dev config` to set up your configuration. Choose the `in-memory` node option for a quick startup without persistent state and avoid additional options like a portal or block explorer unless you want to explore them independently. -To start your local zkSync node, run `npx zksync-cli dev start`. This command spins up a zkSync node in Docker and runs it in the background. Verify the process is running with `docker ps`. +To start your local ZKsync node, run `npx zksync-cli dev start`. This command spins up a ZKsync node in Docker and runs it in the background. Verify the process is running with `docker ps`. > 🗒️ **NOTE**:br -> If Docker isn’t running, the `npx zksync-cli dev start` command will fail. Ensure Docker is running before attempting to start the zkSync node again. +> If Docker isn’t running, the `npx zksync-cli dev start` command will fail. Ensure Docker is running before attempting to start the ZKsync node again. ### Deployment -The zkSync deployment process is similar to previous deployments. We will use the same commands, but this time, we will append the `--zkSync` and `--legacy` flags. Note that the `forge script` command is not well supported in zkSync, so we will use `forge create` instead. +The ZKsync deployment process is similar to previous deployments. We will use the same commands, but this time, we will append the `--zksync` and `--legacy` flags. Note that the `forge script` command is not well supported in ZKsync, so we will use `forge create` instead. ### Conclusion -Setting up a local zkSync node involves a few additional tools, including Docker, Node.js, npm, and zksync-cli: they will help creating a robust zkSync development environment and allowing test and deployment of smart contracts on a zkSync local chain. +Setting up a local ZKsync node involves a few additional tools, including Docker, Node.js, npm, and zksync-cli: they will help creating a robust ZKsync development environment and allowing test and deployment of smart contracts on a ZKsync local chain. diff --git a/courses/foundry/1-foundry-simple-storage/27-zksync-local-deploy/+page.md b/courses/foundry/1-foundry-simple-storage/27-zksync-local-deploy/+page.md index 08b5baaf..2d2ea270 100644 --- a/courses/foundry/1-foundry-simple-storage/27-zksync-local-deploy/+page.md +++ b/courses/foundry/1-foundry-simple-storage/27-zksync-local-deploy/+page.md @@ -1,14 +1,14 @@ --- -title: zkSync Local Deployment +title: ZKsync Local Deployment --- _Follow along with the video_ --- -In this lesson, we are going to deploy the contract `SimpleStorage.sol` on a **zkSync local chain**. +In this lesson, we are going to deploy the contract `SimpleStorage.sol` on a **ZKsync local chain**. -We start by verifying that the Forge version we are using is correct. By running the `forge --version` command it confirms that we are on version 0.2: this indicates we are using the right Foundry zkSync edition. +We start by verifying that the Forge version we are using is correct. By running the `forge --version` command it confirms that we are on version 0.2: this indicates we are using the right Foundry ZKsync edition. Next, we proceed with creating a `SimpleStorage` contract using the command: @@ -16,7 +16,7 @@ Next, we proceed with creating a `SimpleStorage` contract using the command: forge create src/SimpleStorage.sol:SimpleStorage --rpc_url --private_key --legacy --zksync ``` -Here, `` represents zkSync node address, such as `http://127.0.0.1:8011`. +Here, `` represents ZKsync node address, such as `http://127.0.0.1:8011`. > 👀❗**IMPORTANT**:br > Including private keys directly in commands is not a safe practice. diff --git a/courses/foundry/1-foundry-simple-storage/28-tx-types/+page.md b/courses/foundry/1-foundry-simple-storage/28-tx-types/+page.md index bb0bbf9d..e1565ff2 100644 --- a/courses/foundry/1-foundry-simple-storage/28-tx-types/+page.md +++ b/courses/foundry/1-foundry-simple-storage/28-tx-types/+page.md @@ -8,29 +8,29 @@ _Follow along with the video_ ### Introduction -In this lesson, we will explore the different transaction types within the ZK Sync VM and EVM ecosystems. +In this lesson, we will explore the different transaction types within the ZKsync VM and EVM ecosystems. ### `/broadcast` Folder -When deploying to a zkSync local node, a `/broadcast` folder will be created and it will contain detailed information about the **deployment transactions**. Inside this folder, you will find subfolders named after specific deployment chain IDs, such as **`260`** for ZK Sync and **`31337`** for Anvil. These subfolders store the data of the transactions executed during the deployment process. +When deploying to a ZKsync local node, a `/broadcast` folder will be created and it will contain detailed information about the **deployment transactions**. Inside this folder, you will find subfolders named after specific deployment chain IDs, such as **`260`** for ZKsync and **`31337`** for Anvil. These subfolders store the data of the transactions executed during the deployment process. -By examining both the `run-latest.json` file in these folders, we can observe different **transaction types** for each transaction within a chain. For instance, transactions on the Anvil chain might be labeled as type **`0x2`**, while those on the zkSync chain will be of type ** `0x0`**. Deploying a smart contract on the EVM without the `--legacy` flag results in a default transaction type of `0x2`. Adding the `--legacy` flag changes it to type `0x0`. +By examining both the `run-latest.json` file in these folders, we can observe different **transaction types** for each transaction within a chain. For instance, transactions on the Anvil chain might be labeled as type **`0x2`**, while those on the ZKsync chain will be of type **`0x0`**. Deploying a smart contract on the EVM without the `--legacy` flag results in a default transaction type of `0x2`. Adding the `--legacy` flag changes it to type `0x0`. -The EVM and ZK Sync ecosystems support multiple transaction types to accommodate various Ethereum Improvement Proposals (EIPs). Initially, Ethereum had only one transaction type (`0x0` legacy), but as the ecosystem evolved, multiple types were introduced through various EIPs. Subsequent types include type 1, which introduces an _access list_ of addresses and keys, and type 2, also known as [EIP 1559](https://eips.ethereum.org/EIPS/eip-1559) transactions. +The EVM and ZKsync ecosystems support multiple transaction types to accommodate various Ethereum Improvement Proposals (EIPs). Initially, Ethereum had only one transaction type (`0x0` legacy), but as the ecosystem evolved, multiple types were introduced through various EIPs. Subsequent types include type 1, which introduces an _access list_ of addresses and keys, and type 2, also known as [EIP 1559](https://eips.ethereum.org/EIPS/eip-1559) transactions. > 👀❗**IMPORTANT**:br > This `0x2` type is the current default type for the EVM. -Additionally, ZK Sync introduces its [unique transaction type](https://docs.zksync.io/zk-stack/concepts/transaction-lifecycle#eip-712-0x71), the type `113` (`0x71` in hex), which can enable features like [account abstraction](https://docs.zksync.io/build/developer-reference/account-abstraction/). +Additionally, ZKsync introduces its [unique transaction type](https://docs.zksync.io/zk-stack/concepts/transaction-lifecycle#eip-712-0x71), the type `113` (`0x71` in hex), which can enable features like [account abstraction](https://docs.zksync.io/build/developer-reference/account-abstraction/). > 💡 **TIP**:br -> The `forge script` command will work in some scenarios, but it’s not entirely clear where it might fail. For the purpose of this course, we will assume scripting does not work while working with Sync. +> The `forge script` command will work in some scenarios, but it’s not entirely clear where it might fail. For the purpose of this course, we will assume scripting does not work while working with ZKsync. ### Resources -- [zkSync documentation](https://docs.zksync.io/zk-stack/concepts/transaction-lifecycle#transaction-types) about transaction types +- [ZKsync documentation](https://docs.zksync.io/zk-stack/concepts/transaction-lifecycle#transaction-types) about transaction types - [Cyfrin Blog on EIP-4844](https://www.cyfrin.io/blog/what-is-eip-4844-proto-danksharding-and-blob-transactions) ### Conclusion -The ZK Sync VM and EVM ecosystems support various transaction types to meet different EIP requirements. By examining deployment folders and understanding the use of flags like `--legacy`, we can effectively distinguish between these transaction types. +The ZKsync VM and EVM ecosystems support various transaction types to meet different EIP requirements. By examining deployment folders and understanding the use of flags like `--legacy`, we can effectively distinguish between these transaction types. diff --git a/courses/foundry/1-foundry-simple-storage/29-why-l2/+page.md b/courses/foundry/1-foundry-simple-storage/29-why-l2/+page.md index a4deee59..ceaa6b3f 100644 --- a/courses/foundry/1-foundry-simple-storage/29-why-l2/+page.md +++ b/courses/foundry/1-foundry-simple-storage/29-why-l2/+page.md @@ -8,11 +8,11 @@ _Follow along with the video_ ### Introduction -In previous lessons, we deployed to the Sepolia testnet and started working with the Layer 2 solution ZK Sync. Deploying to Sepolia simulates deployment to the Ethereum mainnet, offering a comprehensive understanding of Layer 1 deployments. However, it's important to note that most projects today prefer deploying to Layer 2 solutions rather than directly to Ethereum due to the high costs associated with deployments. +In previous lessons, we deployed to the Sepolia testnet and started working with the Layer 2 solution ZKsync. Deploying to Sepolia simulates deployment to the Ethereum mainnet, offering a comprehensive understanding of Layer 1 deployments. However, it's important to note that most projects today prefer deploying to Layer 2 solutions rather than directly to Ethereum due to the high costs associated with deployments. ### Gas Usage -When deploying to a ZK Sync local node, a `/broadcast` folder is created, containing a lot of detailed deployment transaction information. For instance, in our `run-latest.json` file, we can see the `gasUsed` value and we can convert this hexadecimal number `0x5747A` to its decimal equivalent by typing `cast to base 0x5747A dec`. This conversion allows us to estimate the deployment cost on the Ethereum mainnet. By checking recent gas prices on Etherscan, we can calculate the total cost using the formula: +When deploying to a ZKsync local node, a `/broadcast` folder is created, containing a lot of detailed deployment transaction information. For instance, in our `run-latest.json` file, we can see the `gasUsed` value and we can convert this hexadecimal number `0x5747A` to its decimal equivalent by typing `cast to base 0x5747A dec`. This conversion allows us to estimate the deployment cost on the Ethereum mainnet. By checking recent gas prices on Etherscan, we can calculate the total cost using the formula: ``` Total Cost = Gas Used * Gas Price @@ -20,11 +20,11 @@ Total Cost = Gas Used * Gas Price We can see this total cost in the deployment transaction on [Sepolia Etherscan](https://sepolia.etherscan.io/tx/0xc496b9d30df33aa9285ddd384c14ce2a58eef470898b5cda001d0f4a21b017f6), under the `Transaction Fee` section. In this case, `357,498` gas will costs `0.000279288255846978` ETH, which today is equivalent to $7. -Deploying even a minimal contract like `SimpleStorage` on Ethereum can be expensive. Larger contracts, with thousands of lines of code, can cost thousands of dollars. This is why many developers prefer deploying to Layer 2 solutions like ZK Sync, which offer the same security as Ethereum but at a fraction of the cost. +Deploying even a minimal contract like `SimpleStorage` on Ethereum can be expensive. Larger contracts, with thousands of lines of code, can cost thousands of dollars. This is why many developers prefer deploying to Layer 2 solutions like ZKsync, which offer the same security as Ethereum but at a fraction of the cost. -### Deploying to ZK Sync Sepolia +### Deploying to ZKsync Sepolia -Deploying to ZK Sync Sepolia is similar to deploying to a ZK Sync local node. You can retrieve a ZK Sync Sepolia RPC URL from [Alchemy](https://www.alchemy.com/) by creating a new app based on the ZK Sepolia network. Then, you can proceed to add the `ZKSYNC_RPC_URL` to your `.env` configuration. +Deploying to ZKsync Sepolia is similar to deploying to a ZKsync local node. You can retrieve a ZKsync Sepolia RPC URL from [Alchemy](https://www.alchemy.com/) by creating a new app based on the ZKsepolia network. Then, you can proceed to add the `ZKSYNC_RPC_URL` to your `.env` configuration. > 🗒️ **NOTE**:br -> To understand the cost benefits of Layer 2 solutions, visit [L2Fees.info](https://l2fees.info) and compare the significant cost differences between sending a transaction on Ethereum and ZK Sync Era. +> To understand the cost benefits of Layer 2 solutions, visit [L2Fees.info](https://l2fees.info) and compare the significant cost differences between sending a transaction on Ethereum and ZKsync Era. diff --git a/courses/foundry/1-foundry-simple-storage/3-development-environment-setup-windows/+page.md b/courses/foundry/1-foundry-simple-storage/3-development-environment-setup-windows/+page.md index 0b87e8ff..4863b126 100644 --- a/courses/foundry/1-foundry-simple-storage/3-development-environment-setup-windows/+page.md +++ b/courses/foundry/1-foundry-simple-storage/3-development-environment-setup-windows/+page.md @@ -21,7 +21,7 @@ You can open up multiple terminals at the same time running different shells ran Use the `+` button to create a new terminal or the trash button to kill the current terminal. -Whether you are learning **foundry** for development work or security work, moving fast is one of the keys to efficiency. VS Code is very versatile in terms of keyboard shortcuts, you can learn more [here](https://code.visualstudio.com/docs/getstarted/keybindings). +Whether you are learning **Foundry** for development work or security work, moving fast is one of the keys to efficiency. VS Code is very versatile in terms of keyboard shortcuts, you can learn more [here](https://code.visualstudio.com/docs/getstarted/keybindings). ### What is WSL? diff --git a/courses/foundry/1-foundry-simple-storage/30-alchemy-mempool/+page.md b/courses/foundry/1-foundry-simple-storage/30-alchemy-mempool/+page.md index 61986c94..03a1dcb3 100644 --- a/courses/foundry/1-foundry-simple-storage/30-alchemy-mempool/+page.md +++ b/courses/foundry/1-foundry-simple-storage/30-alchemy-mempool/+page.md @@ -43,10 +43,10 @@ Once you have signed in, create a new application. Next, give your application a name and a description. Then, select a chain and network. Alchemy currently supports the majority of EVM-compatible chains, including: - Ethereum -- Polygon (POS) -- Zkevm +- Polygon PoS +- Polygon zkEVM - Optimism -- Astar +- Arbitrum - Solana (non-EVM chain) ## The Application-Specific Dashboard diff --git a/courses/foundry/1-foundry-simple-storage/31-summary-congratulations/+page.md b/courses/foundry/1-foundry-simple-storage/31-summary-congratulations/+page.md index 049d8061..fdffe889 100644 --- a/courses/foundry/1-foundry-simple-storage/31-summary-congratulations/+page.md +++ b/courses/foundry/1-foundry-simple-storage/31-summary-congratulations/+page.md @@ -28,7 +28,7 @@ We obtained practical knowledge on how to compile code in Foundry and write a So ## Understanding Contract Deployment and Interaction on the Blockchain -We delved into the automation of contract deployments to a blockchain. Post-deployment, we interacted with them using the `Cast` keyword and `send` to make transactions, then `Cast call` to read from those contracts. +We delved into the automation of contract deployments to a blockchain. Post-deployment, we interacted with them using the `cast` keyword and `send` to make transactions, then `cast call` to read from those contracts. Moreover, the knowledge on how to auto format contracts with `Forge format` was acquired. We also learnt the painstaking yet rewarding manual method of verifying our contracts on the blockchain. diff --git a/courses/foundry/1-foundry-simple-storage/6-foundry-install/+page.md b/courses/foundry/1-foundry-simple-storage/6-foundry-install/+page.md index 329355c9..2868dbb5 100644 --- a/courses/foundry/1-foundry-simple-storage/6-foundry-install/+page.md +++ b/courses/foundry/1-foundry-simple-storage/6-foundry-install/+page.md @@ -36,7 +36,6 @@ The command would look something like this: ```bash curl -L https://foundry.paradigm.xyz | bash - ``` Hit `Enter` after pasting this in your terminal. @@ -73,7 +72,8 @@ Try typing `forge --version` into your terminal. Have you received an unwelcome Note: Most of the time the `bashrc` file gets loaded automatically. However, if this doesn't apply to your setup, the following lines can add the required command to the end of your `Bash profile`. This will ensure that your `bashrc` file loads by default. ```bash -cd ~echo 'source /home/user/.bashrc' >> ~/.bash_profile +cd ~ +echo 'source /home/user/.bashrc' >> ~/.bash_profile ``` > this depends on your operating system, please check foundry docs to see detailed instructions. diff --git a/courses/foundry/1-foundry-simple-storage/7-setup-your-vscode/+page.md b/courses/foundry/1-foundry-simple-storage/7-setup-your-vscode/+page.md index 616f1c70..48f14854 100644 --- a/courses/foundry/1-foundry-simple-storage/7-setup-your-vscode/+page.md +++ b/courses/foundry/1-foundry-simple-storage/7-setup-your-vscode/+page.md @@ -31,16 +31,14 @@ VS Code will handle the download and installation process. **That's it! The extension should be ready to use within VS Code.** - - ### Integrating AI into our work +### Integrating AI into our work - One of the best extensions that integrates AI in our development is GitHub Copilot +One of the best extensions that integrates AI in our development is GitHub Copilot - Although it's a premium service, its intuitive AI-powered code autocomplete feature could be a game-changer for you. Of course, you can choose to go with other AI extensions based on your preferences. - - You can download GitHub Copilot [here](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot). More details and answers for your GitHub Copilot-related questions are available [here](https://github.com/features/copilot/?editor=vscode#faq). +Although it's a premium service, its intuitive AI-powered code autocomplete feature could be a game-changer for you. Of course, you can choose to go with other AI extensions based on your preferences. +You can download GitHub Copilot [here](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot). More details and answers for your GitHub Copilot-related questions are available [here](https://github.com/features/copilot/?editor=vscode#faq). ### Other important VS Code Tips @@ -66,4 +64,4 @@ cd foundry-f23 `mkdir` creates a directory or subdirectory. `cd` changes the directory. -Moving forward, it's advisable to keep all your repositories in this folder. Thus, you'll always have a place to reference all your code. \ No newline at end of file +Moving forward, it's advisable to keep all your repositories in this folder. Thus, you'll always have a place to reference all your code. diff --git a/courses/foundry/1-foundry-simple-storage/9-create-a-new-foundry-project-wsl/+page.md b/courses/foundry/1-foundry-simple-storage/9-create-a-new-foundry-project-wsl/+page.md index e13dfc34..8753fcd6 100644 --- a/courses/foundry/1-foundry-simple-storage/9-create-a-new-foundry-project-wsl/+page.md +++ b/courses/foundry/1-foundry-simple-storage/9-create-a-new-foundry-project-wsl/+page.md @@ -12,7 +12,7 @@ Hallo 👋🏻, I'm Vasily and I'll be your instructor for all Windows developme ### WSL setup -Microsoft has singificantly inproved its development environment support in recent years. However, for _smart contract development_, installing dependencies can sometimes be tricky. To streamline this process, we will use the **Windows Subsystem for Linux (WSL)**: this is a better option because it enables a full-fledged _unix-like console_ on your Windows machine, simplifying the use of tools and utilities commonly found in unix-based environments. This setup ensures compatibility with all the code that runs on unix-based systems like macOS and Linux. +Microsoft has significantly improved its development environment support in recent years. However, for _smart contract development_, installing dependencies can sometimes be tricky. To streamline this process, we will use the **Windows Subsystem for Linux (WSL)**: this is a better option because it enables a full-fledged _unix-like console_ on your Windows machine, simplifying the use of tools and utilities commonly found in unix-based environments. This setup ensures compatibility with all the code that runs on unix-based systems like macOS and Linux. To install WSL, you can begin by opening the Windows terminal. On Windows 11, press the Windows key, type "terminal," and hit `enter`. On Windows 10, you need to install the Windows terminal from the Microsoft Store (select the official app from Microsoft Corporation). diff --git a/courses/foundry/2-foundry-fund-me/10-deploy-a-mock-pricefeed/+page.md b/courses/foundry/2-foundry-fund-me/10-deploy-a-mock-pricefeed/+page.md index fc5acfc3..86742e2f 100644 --- a/courses/foundry/2-foundry-fund-me/10-deploy-a-mock-pricefeed/+page.md +++ b/courses/foundry/2-foundry-fund-me/10-deploy-a-mock-pricefeed/+page.md @@ -1,5 +1,6 @@ --- title: Deploy a mock priceFeed +--- _Follow along with this video:_ @@ -9,7 +10,7 @@ _Follow along with this video:_ In the previous lesson, we refactored our contracts to avoid being forced to use Sepolia every single time when we ran tests. The problem is we didn't quite fix this aspect. We made our contracts more flexible by changing everything for us to input the `priceFeed` address only once. We can do better! -It is very important to be able to run our all tests locally. We will do this using a `mock contract`. +It is very important to be able to run our all tests locally. We will do this using a **mock contract**. Before we dive into the code, let's emphasize why this practice is so beneficial. By creating a local testing environment, you reduce your chances of breaking anything in the refactoring process, as you can test all changes before they go live. No more hardcoding of addresses and no more failures when you try to run a test without a forked chain. As a powerful yet simple tool, a mock contract allows you to simulate the behavior of a real contract without the need to interact with a live blockchain. @@ -21,7 +22,7 @@ Please create a new file in your `script` folder called `HelperConfig.s.sol`. He The start: -```javascript +```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.19; @@ -31,28 +32,26 @@ import {Script} from "forge-std/Script.sol"; contract HelperConfig { // If we are on a local Anvil, we deploy the mocks // Else, grab the existing address from the live network - } ``` Copy the following functions inside the contract: -```javascript +```solidity +struct NetworkConfig { + address priceFeed; // ETH/USD price feed address +} - struct NetworkConfig { - address priceFeed; // ETH/USD price feed address - } +function getSepoliaEthConfig() public pure returns (NetworkConfig memory) { + NetworkConfig memory sepoliaConfig = NetworkConfig({ + priceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306 + }); + return sepoliaConfig; +} - function getSepoliaEthConfig() public pure returns (NetworkConfig memory){ - NetworkConfig memory sepoliaConfig = NetworkConfig({ - priceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306 - }); - return sepoliaConfig; - } +function getAnvilEthConfig() public pure returns (NetworkConfig memory) { - function getAnvilEthConfig() public pure returns (NetworkConfig memory){ - - } +} ``` We decided to structure the information we need depending on the chain we are testing on. We use a `struct` to hold this information for every chain. You might think that we could have gotten away with a simple `address variable` but that changes if we need to store multiple addresses or even more blockchain-specific information. @@ -65,20 +64,20 @@ First of all, we need to be aware of the chain we are using. We can do this in t Update the `HelperConfig` as follows: -```javascript - NetworkConfig public activeNetworkConfig; +```solidity +NetworkConfig public activeNetworkConfig; - struct NetworkConfig { - address priceFeed; // ETH/USD price feed address - } +struct NetworkConfig { + address priceFeed; // ETH/USD price feed address +} - constructor(){ - if (block.chainid == 11155111) { - activeNetowrkConfig = getSepoliaEthConfig(); - } else { - activeNetowrkConfig = getAnvilEthConfig(); - } +constructor(){ + if (block.chainid == 11155111) { + activeNetworkConfig = getSepoliaEthConfig(); + } else { + activeNetworkConfig = getAnvilEthConfig(); } +} ``` As you can see, we've defined a new state variable, called activeNetworkConfig which will be the struct that gets queried for blockchain-specific information. We will check the `block.chainId` at the constructor level, and depending on that value we select the appropriate config. @@ -91,13 +90,12 @@ Let's update the `DeployFundMe.s.sol` to use our newly created `HelperConfig`. Add the following before the `vm.startBroadcast` line inside the `run` function: -```javascript - // The next line runs before the vm.startBroadcast() is called - // This will not be deployed because the `real` signed txs are happening - // between the start and stop Broadcast lines. - HelperConfig helperConfig = new HelperConfig(); - address ethUsdPriceFeed = helperConfig.activeNetworkConfig(); - +```solidity +// The next line runs before the vm.startBroadcast() is called +// This will not be deployed because the `real` signed txs are happening +// between the start and stop Broadcast lines. +HelperConfig helperConfig = new HelperConfig(); +address ethUsdPriceFeed = helperConfig.activeNetworkConfig(); ``` Run the `forge test --fork-url $SEPOLIA_RPC_URL` command to check everything is fine. All tests should pass. @@ -108,4 +106,4 @@ Now that we've configured it for one chain, Sepolia, we can do the same with any This type of flexibility elevates your development game to the next level. Being able to easily test your project on different chains just by changing your RPC_URL is an ability that differentiates you from a lot of solidity devs who fumble around with hardcoded addresses. -In the next lessons, we will learn how to use Anvil in our current setup. Stay tuned. \ No newline at end of file +In the next lessons, we will learn how to use Anvil in our current setup. Stay tuned. diff --git a/courses/foundry/2-foundry-fund-me/11-refactoring-the-mock-smart-contract/+page.md b/courses/foundry/2-foundry-fund-me/11-refactoring-the-mock-smart-contract/+page.md index ce2f4466..90350e48 100644 --- a/courses/foundry/2-foundry-fund-me/11-refactoring-the-mock-smart-contract/+page.md +++ b/courses/foundry/2-foundry-fund-me/11-refactoring-the-mock-smart-contract/+page.md @@ -1,5 +1,6 @@ --- title: Refactoring the mock smart contract +--- _Follow along with this video:_ @@ -17,11 +18,11 @@ First of all, we need to make sure we import `Script.sol` in the `HelperConfig.s Update your start of the `HelperConfig.s.sol` file as follows: -```javascript +```solidity import {Script} from "forge-std/Script.sol"; contract HelperConfig is Script { - +} ``` In order to be able to deploy a mock contract we need ... a mock contract. So, in `test` folder please create a new folder called `mocks`. Inside `mocks` create a new file `MockV3Aggregator.sol`. Rewriting the AggregatorV3 as a mock is not the easiest task out there. Please copy the contents of [this contract](https://github.com/Cyfrin/foundry-fund-me-f23/blob/main/test/mock/MockV3Aggregator.sol) into your newly created file. @@ -32,26 +33,27 @@ We need to import this in our `HelperConfig.s.sol` file and deploy it in the `ge Perform the following changes in `HelperConfig`: -```javascript +```solidity import {MockV3Aggregator} from "../test/mocks/MockV3Aggregator.sol"; -[...] - // In state variables section - MockV3Aggregator mockPriceFeed; [...] - function getAnvilEthConfig() public returns (NetworkConfig memory){ +// In state variables section +MockV3Aggregator mockPriceFeed; + +[...] - vm.startBroadcast(); - mockPriceFeed = new MockV3Aggregator(8, 2000e8); - vm.stopBroadcast(); +function getAnvilEthConfig() public returns (NetworkConfig memory) { + vm.startBroadcast(); + mockPriceFeed = new MockV3Aggregator(8, 2000e8); + vm.stopBroadcast(); - NetworkConfig memory anvilConfig = NetworkConfig({ - priceFeed: address(mockPriceFeed) - }); + NetworkConfig memory anvilConfig = NetworkConfig({ + priceFeed: address(mockPriceFeed) + }); - return anvilConfig; - } + return anvilConfig; +} ``` -More testing and refactorings in the next lessons! \ No newline at end of file +More testing and refactorings in the next lessons! diff --git a/courses/foundry/2-foundry-fund-me/12-how-to-refactor-magic-numbers/+page.md b/courses/foundry/2-foundry-fund-me/12-how-to-refactor-magic-numbers/+page.md index 4a1ca662..50a01b92 100644 --- a/courses/foundry/2-foundry-fund-me/12-how-to-refactor-magic-numbers/+page.md +++ b/courses/foundry/2-foundry-fund-me/12-how-to-refactor-magic-numbers/+page.md @@ -1,5 +1,6 @@ --- title: How to refactor magic number +--- _Follow along with this video:_ @@ -7,7 +8,7 @@ _Follow along with this video:_ ### Magic numbers or `What was this exactly?` -Magic numbers refer to literal values directly included in the code without any explanation or context. These numbers can appear anywhere in the code, but they're particularly problematic when used in calculations or comparisons. By using magic numbers you ensure your smart contract suffers from `Reduced Readability`, `Increased Maintenance Difficulty` and `Debugging Challenges`. You also make your work extremely prone to error, imagine you used the same magic number in 10 places and you want to change it. Will you remember all the 9 places or will you change it only in 8? +Magic numbers refer to literal values directly included in the code without any explanation or context. These numbers can appear anywhere in the code, but they're particularly problematic when used in calculations or comparisons. By using magic numbers you ensure your smart contract suffers from **Reduced Readability**, **Increased Maintenance Difficulty** and **Debugging Challenges**. You also make your work extremely prone to error, imagine you used the same magic number in 10 places and you want to change it. Will you remember all the 9 places or will you change it only in 8? **Don't be like that.** @@ -19,7 +20,7 @@ Open `HelperConfig.s.sol`, go to the `getAnvilEthConfig` function and delete the At the top of the `HelperConfig` contract create two new variables: -```javascript +```solidity uint8 public constant DECIMALS = 8; int256 public constant INITIAL_PRICE = 2000e8; ``` @@ -28,19 +29,18 @@ int256 public constant INITIAL_PRICE = 2000e8; Now replace the deleted magic numbers with the newly created variables. -```javascript - function getAnvilEthConfig() public returns (NetworkConfig memory){ - - vm.startBroadcast(); - mockPriceFeed = new MockV3Aggregator(DECIMALS, INITIAL_PRICE); - vm.stopBroadcast(); +```solidity +function getAnvilEthConfig() public returns (NetworkConfig memory) { + vm.startBroadcast(); + mockPriceFeed = new MockV3Aggregator(DECIMALS, INITIAL_PRICE); + vm.stopBroadcast(); - NetworkConfig memory anvilConfig = NetworkConfig({ - priceFeed: address(mockPriceFeed) - }); + NetworkConfig memory anvilConfig = NetworkConfig({ + priceFeed: address(mockPriceFeed) + }); - return anvilConfig; - } + return anvilConfig; +} ``` -Awesome! Let's keep refactoring! \ No newline at end of file +Awesome! Let's keep refactoring! diff --git a/courses/foundry/2-foundry-fund-me/13-refactoring-the-mock-smart-contrat-pt2/+page.md b/courses/foundry/2-foundry-fund-me/13-refactoring-the-mock-smart-contrat-pt2/+page.md index 1ac8c994..4ea61331 100644 --- a/courses/foundry/2-foundry-fund-me/13-refactoring-the-mock-smart-contrat-pt2/+page.md +++ b/courses/foundry/2-foundry-fund-me/13-refactoring-the-mock-smart-contrat-pt2/+page.md @@ -1,5 +1,6 @@ --- title: Refactoring the mock smart contract pt.2 +--- _Follow along with this video:_ @@ -13,10 +14,10 @@ Remember how addresses that were declared in state, but weren't attributed a val Right below the `function getAnvilEthConfig() public returns ...` line add the following: -```javascript - if (activeNetworkConfig.priceFeed != address(0)) { - return activeNetworkConfig; - } +```solidity +if (activeNetworkConfig.priceFeed != address(0)) { + return activeNetworkConfig; +} ``` `getAnvilEthConfig` is not necessarily the best name. We are deploying something inside it, which means we are creating a new `mockPriceFeed`. Let's rename the function to `getOrCreateAnvilEthConfig`. Replace the name in the constructor. @@ -26,7 +27,3 @@ Remember how `testPriceFeedVersionIsAccurate` was always failing when we didn't Everything passes! Amazing! Our test just became network agnostic. Next-level stuff! Take a break, come back in 15 minutes and let's go on! - - - - diff --git a/courses/foundry/2-foundry-fund-me/14-foundry-tests-cheatcodes/+page.md b/courses/foundry/2-foundry-fund-me/14-foundry-tests-cheatcodes/+page.md index 15faf1f9..1c57420e 100644 --- a/courses/foundry/2-foundry-fund-me/14-foundry-tests-cheatcodes/+page.md +++ b/courses/foundry/2-foundry-fund-me/14-foundry-tests-cheatcodes/+page.md @@ -1,5 +1,6 @@ --- title: Foundry tests cheatcodes +--- _Follow along with this video:_ @@ -23,11 +24,11 @@ To test point 1 we will use one of the most important cheatcodes: `expectRevert` Open `FundMe.t.sol` and add the following function: -```javascript - function testFundFailsWIthoutEnoughETH() public { - vm.expectRevert(); // <- The next line after this one should revert! If not test fails. - fundMe.fund(); // <- We send 0 value - } +```solidity +function testFundFailsWIthoutEnoughETH() public { + vm.expectRevert(); // <- The next line after this one should revert! If not test fails. + fundMe.fund(); // <- We send 0 value +} ``` We are attempting to fund the contract with `0` value, it reverts and our test passes. @@ -40,28 +41,28 @@ Now that we made those two variables private, we need to write some getters for Please add the following at the end of `FundMe.sol`: -```javascript - /** Getter Functions */ +```solidity +/** Getter Functions */ - function getAddressToAmountFunded(address fundingAddress) public view returns (uint256) { - return s_addressToAmountFunded[fundingAddress]; - } +function getAddressToAmountFunded(address fundingAddress) public view returns (uint256) { + return s_addressToAmountFunded[fundingAddress]; +} - function getFunder(uint256 index) public view returns (address) { - return s_funders[index]; - } +function getFunder(uint256 index) public view returns (address) { + return s_funders[index]; +} ``` Pfeww! Great now we can test points 2 and 3 indicated above: Add the following test in `FundMe.t.sol`: -```javascript - function testFundUpdatesFundDataStructure() public { - fundMe.fund{value: 10 ether}(); - uint256 amountFunded = fundMe.getAddressToAmountFunded(msg.sender); - assertEq(amountFunded, 10 ether); - } +```solidity +function testFundUpdatesFundDataStructure() public { + fundMe.fund{value: 10 ether}(); + uint256 amountFunded = fundMe.getAddressToAmountFunded(msg.sender); + assertEq(amountFunded, 10 ether); +} ``` Run `forge test --mt testFundUpdatesFundDataStructure` in your terminal. @@ -82,7 +83,7 @@ Ok, cool, but who is the actual user that we are going to use in one of the chea Add the following line at the start of your `FundMeTest` contract: -```javascript +```solidity address alice = makeAddr("alice"); ``` @@ -92,13 +93,13 @@ To further increase the readability of our contract, let's avoid using a magic n Back to our test, add the following test in `FundMe.t.sol`: -```javascript - function testFundUpdatesFundDataStructure() public { - vm.prank(alice); - fundMe.fund{value: SEND_VALUE}(); - uint256 amountFunded = fundMe.getAddressToAmountFunded(alice); - assertEq(amountFunded, SEND_VALUE); - } +```solidity +function testFundUpdatesFundDataStructure() public { + vm.prank(alice); + fundMe.fund{value: SEND_VALUE}(); + uint256 amountFunded = fundMe.getAddressToAmountFunded(alice); + assertEq(amountFunded, SEND_VALUE); +} ``` Finally, now let's run `forge test --mt testFundUpdatesFundDataStructure` again. @@ -131,13 +132,13 @@ Foundry to the rescue! There's always a cheatcode to help you overcome your hurd Add the following line at the end of the setup. -```javascript +```solidity vm.deal(alice, STARTING_BALANCE); ``` Declare the `STARTING_BALANCE` as a constant variable up top: -``` +```solidity uint256 constant STARTING_BALANCE = 10 ether; ``` @@ -146,11 +147,3 @@ Let's run `forge test --mt testFundUpdatesFundDataStructure` again. And now it passes. Congratulations! I know a lot of new cheatcodes were introduced in this lesson. Keep in mind that these are the most important cheatcodes there are, and you are going to use them over and over again. Regardless if you are developing or auditing a project, that project will always have at least an `owner` and a `user`. These two would always have different access to different functionalities. Most of the time the user needs some kind of balance, be it ETH or some other tokens. So, making a new address, giving it some balance, and pranking it to act as a caller for a tx will 100% be part of your every test file. - - - - - - - - diff --git a/courses/foundry/2-foundry-fund-me/15-adding-more-coverage-to-the-tests/+page.md b/courses/foundry/2-foundry-fund-me/15-adding-more-coverage-to-the-tests/+page.md index 21813287..9a4c3d18 100644 --- a/courses/foundry/2-foundry-fund-me/15-adding-more-coverage-to-the-tests/+page.md +++ b/courses/foundry/2-foundry-fund-me/15-adding-more-coverage-to-the-tests/+page.md @@ -1,5 +1,6 @@ --- title: Adding more coverage to the tests +--- _Follow along with this video:_ @@ -11,15 +12,15 @@ In the previous lesson, we tested if the `s_addressToAmountFunded` is updated co Add the following test to your `FundMe.t.sol`: -```javascript - function testAddsFunderToArrayOfFunders() public { - vm.startPrank(alice); - fundMe.fund{value: SEND_VALUE}(); - vm.stopPrank(); +```solidity +function testAddsFunderToArrayOfFunders() public { + vm.startPrank(alice); + fundMe.fund{value: SEND_VALUE}(); + vm.stopPrank(); - address funder = fundMe.getFunder(0); - assertEq(funder, alice); - } + address funder = fundMe.getFunder(0); + assertEq(funder, alice); +} ``` What's happening here? We start with our user `alice` who calls `fundMe.fund` in order to fund the contract. Then we use the `getter` function we created in the previous lesson to query what is registered inside the `funders` array at index `0`. We then use the `assertEq` cheatcode to compare the address we queried against `alice`. @@ -32,15 +33,15 @@ Moving on, we should test the `withdraw` function. Let's check that only the own Add the following test to your `FundMe.t.sol`: -```javascript - function testOnlyOwnerCanWithdraw() public { - vm.prank(alice); - fundMe.fund{value: SEND_VALUE}(); +```solidity +function testOnlyOwnerCanWithdraw() public { + vm.prank(alice); + fundMe.fund{value: SEND_VALUE}(); - vm.expectRevert(); - vm.prank(alice); - fundMe.withdraw(); - } + vm.expectRevert(); + vm.prank(alice); + fundMe.withdraw(); +} ``` What's happening here? We start with our user `alice` who calls `fundMe.fund` in order to fund the contract. We then use Alice's address to try and withdraw. Given that Alice is not the owner of the contract, it should fail. That's why we are using the `vm.expectRevert` cheatcode. @@ -53,24 +54,24 @@ As you can see, in both `testAddsFunderToArrayOfFunders` and `testOnlyOwnerCanWi Add the following modifier to your `FundMe.t.sol`: -```javascript - modifier funded() { - vm.prank(alice); - fundMe.fund{value: SEND_VALUE}(); - assert(address(fundMe).balance > 0); - _; - } +```solidity +modifier funded() { + vm.prank(alice); + fundMe.fund{value: SEND_VALUE}(); + assert(address(fundMe).balance > 0); + _; +} ``` We first use the `vm.prank` cheatcode to signal the fact that the next transaction will be called by `alice`. We call `fund` and then we assert that the balance of the `fundMe` contract is higher than 0, if true, it means that Alice's transaction was successful. Every single time we need our contract funded we can use this modifier to do it. Refactor the previous test as follows: -```javascript - function testOnlyOwnerCanWithdraw() public funded { - vm.expectRevert(); - fundMe.withdraw(); - } +```solidity +function testOnlyOwnerCanWithdraw() public funded { + vm.expectRevert(); + fundMe.withdraw(); +} ``` Slim and efficient! @@ -79,10 +80,10 @@ Ok, we've tested that a non-owner cannot withdraw. But can the owner withdraw? To test this we will need a new getter function. Add the following to the `FundMe.sol` file next to the other getter functions: -```javascript - function getOwner() public view returns (address) { - return i_owner; - } +```solidity +function getOwner() public view returns (address) { + return i_owner; +} ``` Make sure to make `i_owner` private. @@ -97,25 +98,26 @@ The arrange-act-assert (AAA) methodology is one of the simplest and most univers We will start our test as usual: -```javascript +```solidity function testWithdrawFromASingleFunder() public funded { +} ``` Now we are in the first stage of the `AAA` methodology: `Arrange` We first need to check the initial balance of the owner and the initial balance of the contract. -```javascript - uint256 startingFundMeBalance = address(fundMe).balance; - uint256 startingOwnerBalance = fundMe.getOwner().balance; +```solidity +uint256 startingFundMeBalance = address(fundMe).balance; +uint256 startingOwnerBalance = fundMe.getOwner().balance; ``` We have what we need to continue with the `Act` stage. -```javascript - vm.startPrank(fundMe.getOwner()); - fundMe.withdraw(); - vm.stopPrank(); +```solidity +vm.startPrank(fundMe.getOwner()); +fundMe.withdraw(); +vm.stopPrank(); ``` Our action stage is comprised of pranking the owner and then calling `withdraw`. @@ -123,15 +125,14 @@ We have reached our final testing part, the `Assert` stage. We need to find out the new balances, both for the contract and the owner. We need to check if these match the expected numbers: -```javascript - uint256 endingFundMeBalance = address(fundMe).balance; - uint256 endingOwnerBalance = fundMe.getOwner().balance; - assertEq(endingFundMeBalance, 0); - assertEq( - startingFundMeBalance + startingOwnerBalance, - endingOwnerBalance - ); - } +```solidity +uint256 endingFundMeBalance = address(fundMe).balance; +uint256 endingOwnerBalance = fundMe.getOwner().balance; +assertEq(endingFundMeBalance, 0); +assertEq( + startingFundMeBalance + startingOwnerBalance, + endingOwnerBalance +); ``` The `endingFundMeBalance` should be `0`, because we just withdrew everything from it. The `owner`'s balance should be the `startingFundMeBalance + startingOwnerBalance` because we withdrew the `fundMe` starting balance. @@ -146,28 +147,28 @@ Ok, we've tested that the owner can indeed `withdraw` when the `fundMe` contract Put the following test in your `FundMe.t.sol`: -```javascript - function testWithdrawFromMultipleFunders() public funded { - uint160 numberOfFunders = 10; - uint160 startingFunderIndex = 1; - for (uint160 i = startingFunderIndex; i < numberOfFunders + startingFunderIndex; i++) { - // we get hoax from stdcheats - // prank + deal - hoax(address(i), SEND_VALUE); - fundMe.fund{value: SEND_VALUE}(); - } - - uint256 startingFundMeBalance = address(fundMe).balance; - uint256 startingOwnerBalance = fundMe.getOwner().balance; - - vm.startPrank(fundMe.getOwner()); - fundMe.withdraw(); - vm.stopPrank(); - - assert(address(fundMe).balance == 0); - assert(startingFundMeBalance + startingOwnerBalance == fundMe.getOwner().balance); - assert((numberOfFunders + 1) * SEND_VALUE == fundMe.getOwner().balance - startingOwnerBalance); +```solidity +function testWithdrawFromMultipleFunders() public funded { + uint160 numberOfFunders = 10; + uint160 startingFunderIndex = 1; + for (uint160 i = startingFunderIndex; i < numberOfFunders + startingFunderIndex; i++) { + // we get hoax from stdcheats + // prank + deal + hoax(address(i), SEND_VALUE); + fundMe.fund{value: SEND_VALUE}(); } + + uint256 startingFundMeBalance = address(fundMe).balance; + uint256 startingOwnerBalance = fundMe.getOwner().balance; + + vm.startPrank(fundMe.getOwner()); + fundMe.withdraw(); + vm.stopPrank(); + + assert(address(fundMe).balance == 0); + assert(startingFundMeBalance + startingOwnerBalance == fundMe.getOwner().balance); + assert((numberOfFunders + 1) * SEND_VALUE == fundMe.getOwner().balance - startingOwnerBalance); +} ``` That seems like a lot! Let's go through it. @@ -207,4 +208,4 @@ Run the test using `forge test --mt testWithdrawFromMultipleFunders`. Run all te Let's run `forge coverage` and see if our coverage table got better. -Congratulations, everything works way better! \ No newline at end of file +Congratulations, everything works way better! diff --git a/courses/foundry/2-foundry-fund-me/16-introduction-to-foundry-chisel/+page.md b/courses/foundry/2-foundry-fund-me/16-introduction-to-foundry-chisel/+page.md index 22315d36..55828a69 100644 --- a/courses/foundry/2-foundry-fund-me/16-introduction-to-foundry-chisel/+page.md +++ b/courses/foundry/2-foundry-fund-me/16-introduction-to-foundry-chisel/+page.md @@ -1,5 +1,6 @@ --- title: Introduction to Foundry Chisel +--- _Follow along with this video:_ @@ -61,4 +62,4 @@ It reverts! Press `Ctrl + C` twice to exit and return to your normal terminal. -To find more about other Chisel functionality please click [here](https://book.getfoundry.sh/reference/chisel/). \ No newline at end of file +To find more about other Chisel functionality, please click [here](https://book.getfoundry.sh/reference/chisel/). diff --git a/courses/foundry/2-foundry-fund-me/17-calculate-withdraw-gas-costs/+page.md b/courses/foundry/2-foundry-fund-me/17-calculate-withdraw-gas-costs/+page.md index 73bd9979..23e50216 100644 --- a/courses/foundry/2-foundry-fund-me/17-calculate-withdraw-gas-costs/+page.md +++ b/courses/foundry/2-foundry-fund-me/17-calculate-withdraw-gas-costs/+page.md @@ -1,5 +1,6 @@ --- title: Calculate Withdraw gas costs +--- _Follow along with this video:_ @@ -11,7 +12,7 @@ Gas refers to a unit that measures the computational effort required to execute An important aspect of smart contract development is making your code efficient to minimize the gas you/other users spend when calling functions. This can have a serious impact on user retention for your protocol. Imagine you have to exchange 0.1 ETH for 300 USDC, but you have to pay 30 USDC in gas fees. No one wants to pay that. It's your duty as a developer to minimize gas consumption. -Now that you understand the importance of minimizing gas consumption ... how do we find out how much gas things cost? +Now that you understand the importance of minimizing gas consumption, how do we find out how much gas things cost? Let's take a closer look at `testWithdrawFromASingleFunder` test. @@ -29,43 +30,45 @@ Etherscan provides a super nice tool that we can use: [https://etherscan.io/gast **Note: The gas price and ETH price illustrated above correspond to the date and time this lesson was written, these vary, please use the links presented above to find out the current gas and ETH price** -Looking closer at the `testWithdrawFromASingleFunder` one can observe that we found out the initial balances, then we called a transaction and then we asserted that `startingFundMeBalance + startingOwnerBalance` matches the expected balance ... but inside that test we called `withdraw` which should have cost gas. Why didn't the gas we paid affect our balances? Simple, for testing purposes the anvil gas price is defaulted to `0` (different from what we talked about above in the case of Ethereum mainnet where the gas price was around `7 gwei`), so it wouldn't interfere with our testing. +Looking closer at the `testWithdrawFromASingleFunder` one can observe that we found out the initial balances, then we called a transaction and then we asserted that `startingFundMeBalance + startingOwnerBalance` matches the expected balance, but inside that test we called `withdraw` which should have cost gas. Why didn't the gas we paid affect our balances? Simple, for testing purposes the Anvil gas price is defaulted to `0` (different from what we talked about above in the case of Ethereum mainnet where the gas price was around `7 gwei`), so it wouldn't interfere with our testing. Let's change that and force the `withdraw` transaction to have a gas price. At the top of your `FundMeTest` contract define the following variable: -```javascript +```solidity uint256 constant GAS_PRICE = 1; ``` and refactor the `testWithdrawFromASingleFunder` function as follows: -```javascript - function testWithdrawFromASingleFunder() public funded { - // Arrange - uint256 startingFundMeBalance = address(fundMe).balance; - uint256 startingOwnerBalance = fundMe.getOwner().balance; - - vm.txGasPrice(GAS_PRICE); - uint256 gasStart = gasleft(); - // Act - vm.startPrank(fundMe.getOwner()); - fundMe.withdraw(); - vm.stopPrank(); - - uint256 gasEnd = gasleft(); - uint256 gasUsed = (gasStart - gasEnd) * tx.gasprice; - console.log("Withdraw consummed: %d gas", gasUsed); - // Assert - uint256 endingFundMeBalance = address(fundMe).balance; - uint256 endingOwnerBalance = fundMe.getOwner().balance; - assertEq(endingFundMeBalance, 0); - assertEq( - startingFundMeBalance + startingOwnerBalance, - endingOwnerBalance - ); - } +```solidity +function testWithdrawFromASingleFunder() public funded { + // Arrange + uint256 startingFundMeBalance = address(fundMe).balance; + uint256 startingOwnerBalance = fundMe.getOwner().balance; + + vm.txGasPrice(GAS_PRICE); + uint256 gasStart = gasleft(); + + // Act + vm.startPrank(fundMe.getOwner()); + fundMe.withdraw(); + vm.stopPrank(); + + uint256 gasEnd = gasleft(); + uint256 gasUsed = (gasStart - gasEnd) * tx.gasprice; + console.log("Withdraw consumed: %d gas", gasUsed); + + // Assert + uint256 endingFundMeBalance = address(fundMe).balance; + uint256 endingOwnerBalance = fundMe.getOwner().balance; + assertEq(endingFundMeBalance, 0); + assertEq( + startingFundMeBalance + startingOwnerBalance, + endingOwnerBalance + ); +} ``` We changed the following: @@ -94,4 +97,4 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.67ms (2.06ms CPU Cool! -Now that we learned how to calculate the amount of gas used, let's learn how to make it better! \ No newline at end of file +Now that we learned how to calculate the amount of gas used, let's learn how to make it better! 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 f3801df0..d314957d 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 @@ -1,5 +1,6 @@ --- title: Introduction to Storage optimization +--- _Follow along with this video:_ @@ -7,7 +8,7 @@ _Follow along with this video:_ ### Optimizing GAS consumption by properly managing Storage -**Storage** is the specific area within the blockchain where data associated with a smart contract is permanently saved. These are the variables that we defined at the top of our contract, before going into functions, also called `state variable` or `global variables`. +**Storage** is the specific area within the blockchain where data associated with a smart contract is permanently saved. These are the variables that we defined at the top of our contract, before going into functions, also called **state variables** or **global variables**. Imagine yourself being in a giant locker room, and in each locker, you have a space of 32 bytes. Each locker (storage space) is numbered/labeled and its number/label acts as the key, in the key-value pair, thus using this number/label we can access what's stored in the locker. Think of state variables as the labels you give to these lockers, allowing you to easily retrieve the information you've stored. But remember, space on this shelf isn't unlimited, and every time you add or remove something, it comes at a computational cost. From the previous lessons, we learned that this computational cost bears the name of `gas`. @@ -33,7 +34,7 @@ The important aspects are the following: Now this seems like a lot, but let's go through some examples: (Try to think about how the storage looks before reading the description) -``` +```solidit uint256 var1 = 1337; uint256 var2 = 9000; uint64 var3 = 0; @@ -43,7 +44,7 @@ How are these stored? In `slot 0` we have `var1`, in `slot 1` we have `var2`, and in `slot 3` we have `var 3`. Because `var 3` only used 8 bytes, we have 24 bytes left in that slot. Let's try another one: -``` +```solidity uint64 var1 = 1337; uint128 var2 = 9000; bool var3 = true; @@ -91,23 +92,23 @@ Mappings and Dynamic Arrays can't be stored in between the state variables as we Make sure that you have the following getter in `FundMe.sol`: -```javascript - function getPriceFeed() public view returns (AggregatorV3Interface) { - return s_priceFeed; - } +```solidity +function getPriceFeed() public view returns (AggregatorV3Interface) { + return s_priceFeed; +} ``` Please add the following function in your `FundMe.t.sol`: -```javascript - 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.logBytes32(value); - } - console.log("PriceFeed address:", address(fundMe.getPriceFeed())); +```solidity +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.logBytes32(value); } + console.log("PriceFeed address:", address(fundMe.getPriceFeed())); +} ``` In the test above we used a new cheatcode: `vm.load`. Its sole purpose is to load the value found in the provided storage slot of the provided address. Read more about it [here](https://book.getfoundry.sh/cheatcodes/load). diff --git a/courses/foundry/2-foundry-fund-me/19-optimise-the-withdraw-function-gas-costs/+page.md b/courses/foundry/2-foundry-fund-me/19-optimise-the-withdraw-function-gas-costs/+page.md index b54c6d46..56d4ca7f 100644 --- a/courses/foundry/2-foundry-fund-me/19-optimise-the-withdraw-function-gas-costs/+page.md +++ b/courses/foundry/2-foundry-fund-me/19-optimise-the-withdraw-function-gas-costs/+page.md @@ -1,5 +1,6 @@ --- title: Optimise the withdraw function gas costs +--- _Follow along with this video:_ @@ -16,13 +17,17 @@ Open a new terminal, and type `anvil` to start a new `anvil` instance. Deploy the `fundMe` contract using the following script: -`forge script DeployFundMe --rpc-url http://127.0.0.1:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast` +```bash +forge script DeployFundMe --rpc-url http://127.0.0.1:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast` +``` Copy the `fundMe` contract address. Run the following command: - -`cast code 0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9` (replace the address here with the address of your newly deployed `fundMe` contract) +(replace the address here with the address of your newly deployed `fundMe` contract) +```bash +cast code 0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9` +``` Ok, the output looks like an extremely big chunk of random numbers and letters. Perfect! @@ -62,17 +67,17 @@ We start with a `for` loop, that is initializing a variable called `funderIndex` Let's rewrite the function. Add the following to your `FundMe.sol`: -```javascript +```solidity function cheaperWithdraw() public onlyOwner { uint256 fundersLength = s_funders.length; for(uint256 funderIndex = 0; funderIndex < fundersLength; funderIndex++) { - address funder = s_funders[funderIndex]; - s_addressToAmountFunded[funder] = 0; + address funder = s_funders[funderIndex]; + s_addressToAmountFunded[funder] = 0; } - s_funders = new address[](0); + s_funders = new address[](0); - (bool callSuccess,) = payable(msg.sender).call{value: address(this).balance}(""); - require(callSuccess, "Call failed"); + (bool callSuccess,) = payable(msg.sender).call{value: address(this).balance}(""); + require(callSuccess, "Call failed"); } ``` @@ -86,27 +91,27 @@ Let's find out how much we saved. Open `FundMe.t.sol`. Let's copy the `testWithdrawFromMultipleFunders` function and replace the `withdraw` function with `cheaperWithdraw`. -``` - function testWithdrawFromMultipleFundersCheaper() public funded { - uint160 numberOfFunders = 10; - uint160 startingFunderIndex = 1; - for (uint160 i = startingFunderIndex; i < numberOfFunders + startingFunderIndex; i++) { - // we get hoax from stdcheats - // prank + deal - hoax(address(i), SEND_VALUE); - fundMe.fund{value: SEND_VALUE}(); - } - - uint256 startingFundMeBalance = address(fundMe).balance; - uint256 startingOwnerBalance = fundMe.getOwner().balance; - - vm.startPrank(fundMe.getOwner()); - fundMe.cheaperWithdraw(); - vm.stopPrank(); - - assert(address(fundMe).balance == 0); - assert(startingFundMeBalance + startingOwnerBalance == fundMe.getOwner().balance); - assert((numberOfFunders + 1) * SEND_VALUE == fundMe.getOwner().balance - startingOwnerBalance); +```solidity +function testWithdrawFromMultipleFundersCheaper() public funded { + uint160 numberOfFunders = 10; + uint160 startingFunderIndex = 1; + for (uint160 i = startingFunderIndex; i < numberOfFunders + startingFunderIndex; i++) { + // we get hoax from stdcheats + // prank + deal + hoax(address(i), SEND_VALUE); + fundMe.fund{value: SEND_VALUE}(); + } + + uint256 startingFundMeBalance = address(fundMe).balance; + uint256 startingOwnerBalance = fundMe.getOwner().balance; + + vm.startPrank(fundMe.getOwner()); + fundMe.cheaperWithdraw(); + vm.stopPrank(); + + assert(address(fundMe).balance == 0); + assert(startingFundMeBalance + startingOwnerBalance == fundMe.getOwner().balance); + assert((numberOfFunders + 1) * SEND_VALUE == fundMe.getOwner().balance - startingOwnerBalance); } ``` @@ -120,4 +125,3 @@ FundMeTest:testWithdrawFromMultipleFundersCheaper() (gas: 534219) As you can see, we saved up 929 gas just by caching one variable. One of the reasons we easily identified this optimization was the use of `s_` in the `s_funders` array declaration. The ability to know, at any time, what comes from storage and what is in memory facilitates this type of optimization. That's why we recommend using the `s_` and `i_` and all upper case for constants, to always know what comes from where. Familiarize yourself with the style guide available [here](https://docs.soliditylang.org/en/v0.8.4/style-guide.html). - diff --git a/courses/foundry/2-foundry-fund-me/2-fund-me-project-setup/+page.md b/courses/foundry/2-foundry-fund-me/2-fund-me-project-setup/+page.md index 0aa044bc..43de5971 100644 --- a/courses/foundry/2-foundry-fund-me/2-fund-me-project-setup/+page.md +++ b/courses/foundry/2-foundry-fund-me/2-fund-me-project-setup/+page.md @@ -8,18 +8,18 @@ _Follow along with this video:_ ## Foreword -Welcome the the second section of `Foundry Fundamentals`. Here we'll cover `Fund Me`, a simple funding contract. +Welcome the second section of `Foundry Fundamentals`. Here we'll cover `Fund Me`, a simple funding contract. You will learn: -- How to push your project to GitHub; -- Write and run amazing tests; -- Advanced deploy scripts, used to deploy on different chains that require different addresses; -- How to use scripts to interact with contracts, so we can easily reproduce our actions; -- How to use a price feed; -- How to use Chisel; -- Smart contract automation; -- How to make our contracts more gas efficient; +- How to push your project to GitHub +- Write and run amazing tests +- Advanced deploy scripts, used to deploy on different chains that require different addresses +- How to use scripts to interact with contracts, so we can easily reproduce our actions +- How to use a price feed +- How to use Chisel +- Smart contract automation +- How to make our contracts more gas efficient - And many more interesting things! Until now, we talked a lot about storage and state, but we didn't delve into what they really mean. We will learn what all these means! @@ -33,10 +33,10 @@ Going through the [repo](https://github.com/Cyfrin/foundry-fund-me-f23) we can s As you can see we are employing some advanced tools/standard naming conventions: -- We use a named error `FundMe__NotOwner();`; -- We use all caps for constants; -- `i_` for immutable variables; -- `s_` for private variables; +- We use a named error `FundMe__NotOwner();` +- We use all caps for constants +- `i_` for immutable variables +- `s_` for private variables Let's clone this project locally. Open your VS Code, and make sure you are in the `foundry-f23` folder, if not use `cd` to navigate to it. diff --git a/courses/foundry/2-foundry-fund-me/20-create-integration-tests/+page.md b/courses/foundry/2-foundry-fund-me/20-create-integration-tests/+page.md index 2d7c19c2..e0166ffc 100644 --- a/courses/foundry/2-foundry-fund-me/20-create-integration-tests/+page.md +++ b/courses/foundry/2-foundry-fund-me/20-create-integration-tests/+page.md @@ -1,5 +1,6 @@ --- title: Create integration tests +--- _Follow along with this video:_ @@ -25,7 +26,9 @@ Each contract will contain one script, and for it to work each needs to inherit In order to properly interact with our `fundMe` contract we would want to interact only with the most recent deployment we made. This task is easily achieved using the `foundry-devops` library. Please install it using the following command: -`forge install Cyfrin/foundry-devops --no-commit` +```bash +forge install Cyfrin/foundry-devops --no-commit +``` Ok, now with that out of the way, let's work on our scripts. @@ -168,4 +171,4 @@ Forge FFI, which stands for Foreign Function Interface, is a cheatcode within th Read more about it [here](https://book.getfoundry.sh/cheatcodes/ffi?highlight=ffi#ffi). -A word of caution: FFI bypasses the normal security checks and limitations of Solidity. By running external commands, you introduce potential security risks if not used carefully. Malicious code within the commands you execute could compromise your setup. Whenever you clone repos or download other projects please make sure they don't have `ffi = true` in their `foundry.toml` file. If they do, we advise you not to run anything before you thoroughly examine where `ffi` is used and what commands is it calling. Stay safe! \ No newline at end of file +A word of caution: FFI bypasses the normal security checks and limitations of Solidity. By running external commands, you introduce potential security risks if not used carefully. Malicious code within the commands you execute could compromise your setup. Whenever you clone repos or download other projects please make sure they don't have `ffi = true` in their `foundry.toml` file. If they do, we advise you not to run anything before you thoroughly examine where `ffi` is used and what commands is it calling. Stay safe! diff --git a/courses/foundry/2-foundry-fund-me/21-automate-your-smart-contracts-actions-makefile/+page.md b/courses/foundry/2-foundry-fund-me/21-automate-your-smart-contracts-actions-makefile/+page.md index e492145b..825ed286 100644 --- a/courses/foundry/2-foundry-fund-me/21-automate-your-smart-contracts-actions-makefile/+page.md +++ b/courses/foundry/2-foundry-fund-me/21-automate-your-smart-contracts-actions-makefile/+page.md @@ -141,7 +141,3 @@ And you just deployed a fresh `FundMe` contract on a fresh `anvil` blockchain. S We could do the same for Sepolia by running `make deploy ARGS="--network sepolia"`. Makefile is amazing! - - - - diff --git a/courses/foundry/2-foundry-fund-me/22-zksync-devops/+page.md b/courses/foundry/2-foundry-fund-me/22-zksync-devops/+page.md index e8a6b845..e379b42f 100644 --- a/courses/foundry/2-foundry-fund-me/22-zksync-devops/+page.md +++ b/courses/foundry/2-foundry-fund-me/22-zksync-devops/+page.md @@ -1,5 +1,5 @@ --- -title: zkSync DevOps +title: ZKsync DevOps --- _Follow along with the video_ @@ -8,11 +8,11 @@ _Follow along with the video_ ### Introduction -There are notable differences between the EVM and zkSync Era VM, as detailed in the [zkSync documentation](https://docs.zksync.io/build/developer-reference/ethereum-differences/evm-instructions). In this lesson, we will explore some DevOps tools designed to help run tests and functions on both VMs. +There are notable differences between the EVM and ZKsync Era VM, as detailed in the [ZKsync documentation](https://docs.zksync.io/build/developer-reference/ethereum-differences/evm-instructions). In this lesson, we will explore some DevOps tools designed to help run tests and functions on both VMs. ### `foundry-devops` Tools -In the `FundMeTest.t.sol` file, certain tests that run on Vanilla Foundry may not work on zkSync Foundry, and vice versa. To address these differences, we will explore two packages from the [foundry-devops](https://github.com/Cyfrin/foundry-devops) repository: `ZkSyncChainChecker` and `FoundryZkSyncChecker`. This lesson will cover how to use these packages effectively. +In the `FundMeTest.t.sol` file, certain tests that run on Vanilla Foundry may not work on ZKsync Foundry, and vice versa. To address these differences, we will explore two packages from the [foundry-devops](https://github.com/Cyfrin/foundry-devops) repository: `ZkSyncChainChecker` and `FoundryZkSyncChecker`. This lesson will cover how to use these packages effectively. ```js import { ZkSyncChainChecker } from "lib/foundry-devops/src/ZkSyncChainChecker.sol"; @@ -21,7 +21,7 @@ import { FoundryZkSyncChecker } from "lib/foundry-devops/src/FoundryZkSyncChecke ### Setting Up ZkSyncDevOps -The file [`test/unit/ZkSyncDevOps.t.sol`](https://github.com/Cyfrin/foundry-fund-me-cu/blob/main/test/unit/ZkSyncDevOps.t.sol) is a minimal test file that shows how tests may fail on the zkSync VM but pass on an EVM, or vice versa. You can follow these steps to set it up: +The file [`test/unit/ZkSyncDevOps.t.sol`](https://github.com/Cyfrin/foundry-fund-me-cu/blob/main/test/unit/ZkSyncDevOps.t.sol) is a minimal test file that shows how tests may fail on the ZKsync VM but pass on an EVM, or vice versa. You can follow these steps to set it up: 1. Copy the content from the GitHub repo into your project's `test/unit` directory, and create a new file named `ZkSyncDevOps.t.sol`. 2. Install any missing dependencies using the command: @@ -52,9 +52,9 @@ You can switch environments between `fundryup` and `fundryup-zksync` to observe forge test --mt testZkSyncChainFails -vvv ``` -will pass in both Foundry environments. However, if you remove the `skipZkSync` modifier, the test will fail on zkSync because the content of the function is not suported on this chain. +will pass in both Foundry environments. However, if you remove the `skipZkSync` modifier, the test will fail on ZKsync because the content of the function is not supported on this chain. -For more details on these modifiers, refer to the [foundry-devops repo](https://github.com/Cyfrin/foundry-devops?tab=readme-ov-file#usage---zksync-checker). The `skipzksync` modifier skips tests on the zkSync chain, while `onlyzksync` runs tests only on a zkSync-based chain. +For more details on these modifiers, refer to the [foundry-devops repo](https://github.com/Cyfrin/foundry-devops?tab=readme-ov-file#usage---zksync-checker). The `skipzksync` modifier skips tests on the ZKsync chain, while `onlyzksync` runs tests only on a ZKsync-based chain. ### Foundry version modifiers diff --git a/courses/foundry/2-foundry-fund-me/23-pushing-to-github/+page.md b/courses/foundry/2-foundry-fund-me/23-pushing-to-github/+page.md index 3f27129d..05792e9d 100644 --- a/courses/foundry/2-foundry-fund-me/23-pushing-to-github/+page.md +++ b/courses/foundry/2-foundry-fund-me/23-pushing-to-github/+page.md @@ -1,5 +1,6 @@ --- title: Pushing to Github +--- _Follow along with this video:_ @@ -11,7 +12,7 @@ What a journey! Congratulations on reaching this far! One of the most important parts of development is sharing the stuff you work on for other people to see and contribute to. If you don't want to do that you still need a system for version control, a place where you save different stages of your project that can be accessed as simple as pressing 3 clicks. As you've guessed by now the thing we'll introduce now is GitHub. -Before doing any other actions, please verify that your .gitignore contains at least the `.env` file, to avoid pushing our keys on the internet, and other things that you consider irrelevant for other people, for example, the information about your deployments. +Before doing any other actions, please verify that your `.gitignore` contains at least the `.env` file, to avoid pushing our keys on the internet, and other things that you consider irrelevant for other people, for example, the information about your deployments. Here is a `.gitignore` example: @@ -37,7 +38,7 @@ docs/ .keystore ``` -Following this point, we will assume that you have your own GitHub account. If not please read [this page](https://docs.github.com/en/get-started/start-your-journey/creating-an-account-on-github) to find out how to get one. Having a GitHub account opens up a ton of possibilities in terms of developing your project or contributing to existing open-source projects. You have the option of forking an existing project and building on top of it, enhancing or adding extra functionality. You can open up issues on existing projects and make contributions to the codebases of other people, there are a lot of stories about people who contributed to other people's code and formed a strong bond and found business partners and friends that way. Moreover, GitHub profiles are crucial when applying for jobs. Safe to say, that having a great GitHub profile that's interesting and stands out can open up a world of opportunities. +Following this point, we will assume that you have your own GitHub account. If not, please read [this page](https://docs.github.com/en/get-started/start-your-journey/creating-an-account-on-github) to find out how to get one. Having a GitHub account opens up a ton of possibilities in terms of developing your project or contributing to existing open-source projects. You have the option of forking an existing project and building on top of it, enhancing or adding extra functionality. You can open up issues on existing projects and make contributions to the codebases of other people, there are a lot of stories about people who contributed to other people's code and formed a strong bond and found business partners and friends that way. Moreover, GitHub profiles are crucial when applying for jobs. Safe to say, that having a great GitHub profile that's interesting and stands out can open up a world of opportunities. If you want to get started or want a quick start, [GitHub](https://docs.github.com/en) docs provide numerous sets of documentation that you can refer to. In this lesson, we will learn how to do all these using our terminal and not the GitHub website interface. @@ -182,4 +183,4 @@ cd 2023-10-PasswordStore code . ``` -Amazing! You can now show off your newly created GitHub project! Click [here](https://twitter.com/intent/tweet?text=I%20just%20made%20my%20first%20Smart%20Contract%20repo%20using%20@solidity_lang,%20foundry,%20@chainlink,%20@AlchemyPlatform,%20and%20more!%0a%0aThanks%20@PatrickAlphaC!!) to tweet about this celebratory moment! Make sure to link your repository! \ No newline at end of file +Amazing! You can now show off your newly created GitHub project! Click [here](https://twitter.com/intent/tweet?text=I%20just%20made%20my%20first%20Smart%20Contract%20repo%20using%20@solidity_lang,%20foundry,%20@chainlink,%20@AlchemyPlatform,%20and%20more!%0a%0aThanks%20@PatrickAlphaC!!) to tweet about this celebratory moment! Make sure to link your repository! diff --git a/courses/foundry/2-foundry-fund-me/24-section-recap/+page.md b/courses/foundry/2-foundry-fund-me/24-section-recap/+page.md index f27fb0b5..46f5114e 100644 --- a/courses/foundry/2-foundry-fund-me/24-section-recap/+page.md +++ b/courses/foundry/2-foundry-fund-me/24-section-recap/+page.md @@ -1,5 +1,6 @@ --- title: Section recap +--- _Follow along with this video:_ @@ -11,16 +12,16 @@ This was a monumental section! And you are a true hero for completing it! Let's quickly go through what we learned in this one: -- We learned more about how to set up a Foundry project; -- We learned how to organize our files into folders, following the convention; -- We learned how to refactor smart contracts to make them modular and chain-agnostic; -- We learned how to write scripts that act as interaction commands; -- We learned more about using mocks; -- We did a bunch of unit tests, we even did an integration test; -- We learned a bit about Makefiles; -- We learned how to create a GitHub repository and push our code in it; -- We also learned how to clone a GitHub repository. +- We learned more about how to set up a Foundry project +- We learned how to organize our files into folders, following the convention +- We learned how to refactor smart contracts to make them modular and chain-agnostic +- We learned how to write scripts that act as interaction commands +- We learned more about using mocks +- We did a bunch of unit tests, we even did an integration test +- We learned a bit about Makefiles +- We learned how to create a GitHub repository and push our code in it +- We also learned how to clone a GitHub repository This was a lot and you deserve a break! Take a walk, play a game and come back for more! -See you in the next section! \ No newline at end of file +See you in the next section! diff --git a/courses/foundry/2-foundry-fund-me/4-finishing-the-setup/+page.md b/courses/foundry/2-foundry-fund-me/4-finishing-the-setup/+page.md index 02b6aaa4..0e105303 100644 --- a/courses/foundry/2-foundry-fund-me/4-finishing-the-setup/+page.md +++ b/courses/foundry/2-foundry-fund-me/4-finishing-the-setup/+page.md @@ -30,9 +30,9 @@ Wait for it to finish. We used `forge install` to ask Forge to install something in our project. What? We specified the path to a GitHub repository, this also could have been a raw URL. What version? Following the path to a GitHub repository you can add an `@` and then you can specify: -- A branch: master; -- A tag: v1.2.3.4 or 0.6.1 in our case; -- A commit: 8e8128; +- A branch: master +- A tag: v1.2.3.4 or 0.6.1 in our case +- A commit: 8e8128 We end the install command with `--no commit` in order to not create a git commit. More on this option later. diff --git a/courses/foundry/2-foundry-fund-me/5-writing-tests-for-your-solidity-smart-contract/+page.md b/courses/foundry/2-foundry-fund-me/5-writing-tests-for-your-solidity-smart-contract/+page.md index ff4e7eb0..f61a244d 100644 --- a/courses/foundry/2-foundry-fund-me/5-writing-tests-for-your-solidity-smart-contract/+page.md +++ b/courses/foundry/2-foundry-fund-me/5-writing-tests-for-your-solidity-smart-contract/+page.md @@ -1,5 +1,6 @@ --- title: Writing tests for your Solidity smart contract +--- _Follow along with this video:_ diff --git a/courses/foundry/2-foundry-fund-me/6-debug-your-solidity-tests/+page.md b/courses/foundry/2-foundry-fund-me/6-debug-your-solidity-tests/+page.md index 0286369b..085eb2af 100644 --- a/courses/foundry/2-foundry-fund-me/6-debug-your-solidity-tests/+page.md +++ b/courses/foundry/2-foundry-fund-me/6-debug-your-solidity-tests/+page.md @@ -1,5 +1,6 @@ --- title: Debug your Solidity tests +--- _Follow along with this video:_ diff --git a/courses/foundry/2-foundry-fund-me/7-advanced-deploy-scripts/+page.md b/courses/foundry/2-foundry-fund-me/7-advanced-deploy-scripts/+page.md index 31242169..c85bb396 100644 --- a/courses/foundry/2-foundry-fund-me/7-advanced-deploy-scripts/+page.md +++ b/courses/foundry/2-foundry-fund-me/7-advanced-deploy-scripts/+page.md @@ -1,5 +1,6 @@ --- title: Advanced deploy scripts +--- _Follow along with this video:_ @@ -7,7 +8,7 @@ _Follow along with this video:_ ### Writing Deploy Scripts -When we went straight to testing we left behind a very important element, deploy scripts. Why is this important you ask? Because we need a certain degree of flexibility that we can't obtain in any other way, let's look through the two files `FundMe.sol` and `PriceConverter.sol`, we can see that both have an address (`0x694AA1769357215DE4FAC081bf1f309aDC325306`) hardcoded for the AggregatorV3Interface. This address is valid, it matches the AggregatorV3 on Sepolia ... but what if we want to test on Anvil? What if we deploy on mainnet or Arbitrum? What then? +When we went straight to testing, we left behind a very important element: deploy scripts. Why is this important you ask? Because we need a certain degree of flexibility that we can't obtain in any other way, let's look through the two files `FundMe.sol` and `PriceConverter.sol`, we can see that both have an address (`0x694AA1769357215DE4FAC081bf1f309aDC325306`) hardcoded for the AggregatorV3Interface. This address is valid, it matches the AggregatorV3 on Sepolia but what if we want to test on Anvil? What if we deploy on mainnet or Arbitrum? What then? The deploy script is the key to overcome this problem! @@ -16,7 +17,7 @@ Create a new file called `DeployFundMe.s.sol` in `script` folder. Please use the We start with stating the SPDX and pragma: ``` -SPDX-License-Identifier: MIT +//SPDX-License-Identifier: MIT pragma solidity 0.8.18; ``` @@ -53,4 +54,4 @@ contract DeployFundMe is Script { Now let's try it with `forge script DeployFundMe`. -Everything was ok! Congrats! \ No newline at end of file +Everything was ok! Congrats! diff --git a/courses/foundry/2-foundry-fund-me/8-running-tests-on-chains-forks/+page.md b/courses/foundry/2-foundry-fund-me/8-running-tests-on-chains-forks/+page.md index be4ce069..bc21c2ec 100644 --- a/courses/foundry/2-foundry-fund-me/8-running-tests-on-chains-forks/+page.md +++ b/courses/foundry/2-foundry-fund-me/8-running-tests-on-chains-forks/+page.md @@ -1,5 +1,6 @@ --- title: Running tests on chains forks +--- _Follow along with this video:_ @@ -58,7 +59,7 @@ Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.29s (536.03ms CPU Nice! -Please keep in mind that forking uses the alchemy API, it's not a good idea to run all your tests on a fork every single time. But, sometimes as in this case, you can't test without. It's very important that our test have a high **coverage**, to ensure all our code is battle tested. +Please keep in mind that forking uses the Alchemy API, it's not a good idea to run all your tests on a fork every single time. But, sometimes as in this case, you can't test without. It's very important that our test have a high **coverage**, to ensure all our code is battle tested. ### Coverage diff --git a/courses/foundry/2-foundry-fund-me/9-refactoring-your-tests/+page.md b/courses/foundry/2-foundry-fund-me/9-refactoring-your-tests/+page.md index 7261a03c..eead0c96 100644 --- a/courses/foundry/2-foundry-fund-me/9-refactoring-your-tests/+page.md +++ b/courses/foundry/2-foundry-fund-me/9-refactoring-your-tests/+page.md @@ -1,5 +1,6 @@ --- title: Refactoring your tests +--- _Follow along with this video:_ @@ -9,44 +10,44 @@ _Follow along with this video:_ The way our code is currently structured is not that flexible. Given the hardcoded Sepolia address we cannot deploy to other chains, and if we wish to do so we would have to come and copy-paste another address everywhere throughout the code. In bigger codebases, with multiple addresses / other items to copy-paste for each deployment (in case we deploy on multiple chains) this update activity is extremely prone to error. We can do better. -To fix this we can make our project more modular, which would help improve de maintainability, testing and deployment. This is done by moving the hardcoded changing variables to the constructor, thus regardless of the chain we deploy our contracts to, we provide the chain-specific elements once, in the constructor, and then we are good to go. +To fix this we can make our project more modular, which would help improve the maintainability, testing and deployment. This is done by moving the hardcoded changing variables to the constructor, thus regardless of the chain we deploy our contracts to, we provide the chain-specific elements once, in the constructor, and then we are good to go. Changing code without changing its functionality bears the name of **refactoring**. Do the following modifications in `FundMe.sol` 1. In the storage variables section create a new variable: -```javascript +```solidity AggregatorV3Interface private s_priceFeed; ``` 2. We need to add this as an input in our constructor and assign it to the state variable. This is done as follows: -```javascript +```solidity constructor(address priceFeed){ i_owner = msg.sender; s_priceFeed = AggregatorV3Interface(priceFeed); } ``` 3. Inside the `getVersion` function, where AggregatorV3Interface is invoked, replace the hardcoded address with the state variable s_priceFeed: -```javascript - function getVersion() public view returns (uint256){ - AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeed); - return priceFeed.version(); - } +```solidity +function getVersion() public view returns (uint256){ + AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeed); + return priceFeed.version(); +} ``` 4. In `PriceConverter.sol` modify the `getPrice` function to take an input `function getPrice(AggregatorV3Interface priceFeed) internal view returns (uint256) {` and delete the `AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);` line; 5. In the same library update `getConversionRate` to take a `priceFeed` input and update the first line to pass the required AggregatorV3Interface to the `getPrice` function: -```javascript - function getConversionRate( - uint256 ethAmount, - AggregatorV3Interface priceFeed - ) internal view returns (uint256) { - uint256 ethPrice = getPrice(priceFeed); - uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000; - // the actual ETH/USD conversion rate, after adjusting the extra 0s. - return ethAmountInUsd; - } +```solidity +function getConversionRate( + uint256 ethAmount, + AggregatorV3Interface priceFeed +) internal view returns (uint256) { + uint256 ethPrice = getPrice(priceFeed); + uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000; + // the actual ETH/USD conversion rate, after adjusting the extra 0s. + return ethAmountInUsd; +} ``` 6. Back in `FundMe.sol` pass the s_priceFeed as input for `getConversionRate` in the `fund` function. @@ -59,12 +60,13 @@ For now, let's hardcode the address `0x694AA1769357215DE4FAC081bf1f309aDC325306` As you've figured out this isn't ideal either. Every time we want to do something from now on do we have to update in both places? Not good. Update the `run` function from the `DeployFundMe` script: -```javascript -function run() external returns (FundMe fundMe) { +```solidity +function run() external returns (FundMe) { vm.startBroadcast(); - fundMe = new FundMe(0x694AA1769357215DE4FAC081bf1f309aDC325306); + FundMe fundMe = new FundMe(0x694AA1769357215DE4FAC081bf1f309aDC325306); vm.stopBroadcast(); -} + return fundMe +} ``` Now when we call run it returns the `FundMe` contract we deployed. @@ -72,16 +74,16 @@ In `FundMe.t.sol`: 1. Let's import the deployment script into the `FundMe.t.sol`. -```javascript +```solidity import {DeployFundMe} from "../script/DeployFundMe.s.sol"; ``` 2. Create a new state variable `DeployFundMe deployFundMe;`; 3. Update the `setUp` function as follows: -```javascript - function setUp() external { - deployFundMe = new DeployFundMe(); - fundMe = deployFundMe.run(); - } +```solidity +function setUp() external { + deployFundMe = new DeployFundMe(); + fundMe = deployFundMe.run(); +} ``` Let's call a `forge test --fork-url $SEPOLIA_RPC_URL` to make sure everything compiles. @@ -92,11 +94,11 @@ When we changed the method of deployment and made it go through the run command **Note**: `vm.startBroadcast` is special, it uses the address that calls the test contract or the address / private key provided as the sender. You can read more about it here. -To account for the way `vm.startBroadcast` works please perform the following modification in `FundMe.t.so`: -``` - function testOwnerIsMsgSender() public { - assertEq(fundMe.i_owner(), msg.sender); - } +To account for the way `vm.startBroadcast` works please perform the following modification in `FundMe.t.sol`: +```solidity +function testOwnerIsMsgSender() public { + assertEq(fundMe.i_owner(), msg.sender); +} ``` Run `forge test --fork-url $SEPOLIA_RPC_URL` again. diff --git a/courses/foundry/3-html-fund-me/1-introduction/+page.md b/courses/foundry/3-html-fund-me/1-introduction/+page.md index e01e8166..a260908c 100644 --- a/courses/foundry/3-html-fund-me/1-introduction/+page.md +++ b/courses/foundry/3-html-fund-me/1-introduction/+page.md @@ -8,4 +8,4 @@ _Follow along with the video_ > To get started, visit the GitHub repository and navigate to the [lesson 8](https://github.com/Cyfrin/html-fund-me-cu) codebase. -In this section, we will delve into how MetaMask or your wallet interacts with websites and ensure that the transactions sent by the wallet are executed as intended. Although we won't be building a full-stack application here, we have plans to launch a comprehensive full-stack course soon. For now, our focus will be on the _HTML FundMe f23 project_, a basic, fully functional website built with raw JavaScript. +In this section, we will delve into how MetaMask or your wallet interacts with websites and ensure that the transactions sent by the wallet are executed as intended. Although we won't be building a full-stack application here, we have plans to launch a comprehensive full-stack course soon. For now, our focus will be on the _HTML FundMe cu project_, a basic, fully functional website built with raw JavaScript. diff --git a/courses/foundry/3-html-fund-me/2-setup/+page.md b/courses/foundry/3-html-fund-me/2-setup/+page.md index fcc3c0c2..d6744169 100644 --- a/courses/foundry/3-html-fund-me/2-setup/+page.md +++ b/courses/foundry/3-html-fund-me/2-setup/+page.md @@ -18,7 +18,7 @@ _Follow along the course with this video._ ### Overview -Let's look at how what we've built interacts with a wallet. Remember, you can find all the code for this lesson [**here**](https://github.com/Cyfrin/html-fund-me-f23). +Let's look at how what we've built interacts with a wallet. Remember, you can find all the code for this lesson [**here**](https://github.com/Cyfrin/html-fund-me-cu). We won't be going over a whole full-stack application here, but the repo above contains a raw front-end you can try to replicate if you would like to challenge yourself. @@ -35,13 +35,13 @@ Now that you've installed Git and created a GitHub in previous lessons, we're go **Step 1:** In our terminal use the command: ```bash -git clone https://github.com/Cyfrin/html-fund-me-f23.git +git clone https://github.com/Cyfrin/html-fund-me-cu.git ``` **Step 2:** Now we can open this in a new instance of VS Code with: ```bash -code html-fund-me-f23 +code html-fund-me-cu ``` In order to spin up a local front end, we're going to use an extension called [**Live Server**](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer). Once installed you can simply press the `Go Live` button in the bottom right. diff --git a/courses/foundry/3-html-fund-me/3-metamask/+page.md b/courses/foundry/3-html-fund-me/3-metamask/+page.md index 3b70cd20..0f8cba1f 100644 --- a/courses/foundry/3-html-fund-me/3-metamask/+page.md +++ b/courses/foundry/3-html-fund-me/3-metamask/+page.md @@ -8,7 +8,7 @@ _Follow along the course with this video._ ### Browser Wallets -The first concept we need to grasp when working with a website in Web3 is that of a browser wallet - in our case Metamask. It's through a wallet like Metamask that we are able to interact with the blockchain and the Web3 ecosystem. +The first concept we need to grasp when working with a website in Web3 is that of a browser wallet - in our case MetaMask. It's through a wallet like MetaMask that we are able to interact with the blockchain and the Web3 ecosystem. We can gain more insight into how this works by right-clicking our `FundMe` website and selecting `inspect`. You can also open this panel by pressing F12. @@ -26,7 +26,7 @@ As seen in the image, there are some properties of this object which are not the > Try inspecting a browser without a browser wallet installed. You'll see that `window.ethereum` doesn't exist! -I recommend reading the [**Metamask documentation**](https://docs.metamask.io/guide/) on the window.ethereum object to learn more. +I recommend reading the [**MetaMask documentation**](https://docs.metamask.io/guide/) on the window.ethereum object to learn more. ### The Code @@ -78,7 +78,7 @@ This grabs the element of the webpage by the `id` we set and then uses the `onCl ### Connecting in Action -Clicking on the `Connect` button on our `html-fund-me` front end, should trigger our Metamask to pop up. From there we can select an account and click connect. +Clicking on the `Connect` button on our `html-fund-me` front end, should trigger our MetaMask to pop up. From there we can select an account and click connect. ::image{src='/html-fundme/2-metamask/metamask3.png' style='width: 75%; height: auto;'} @@ -114,7 +114,7 @@ As before, we're checking for the existence of `window.ethereum` and then .. def `ethers` is a javascript package that simplifies the use and interacation of browser wallets with our code. -What `ethers.BrowserProvider(window.ethereum)` is doing, is deriving the providers Metamask is injecting into our `window.ethereum` object. The providers are the RPC URLs associated with the networks in our Metamask account. +What `ethers.BrowserProvider(window.ethereum)` is doing, is deriving the providers MetaMask is injecting into our `window.ethereum` object. The providers are the RPC URLs associated with the networks in our MetaMask account. ::image{src='/html-fundme/2-metamask/metamask5.png' style='width: 75%; height: auto;'} @@ -122,17 +122,17 @@ When we call functions on our front-end. We're effectively making API calls via ### Trying it Out -In order to get some experience trying this ourselves, we'll need to set up the backend of our project and import our anvil account into Metamask. +In order to get some experience trying this ourselves, we'll need to set up the backend of our project and import our anvil account into MetaMask. Open your foundry-fund-me directory in VS Code and in your terminal run `anvil`. -This should spin up a local test chain for you. Copy one of the mock private keys it provides you in the terminal, we'll need this to import the account into our Metamask wallet. +This should spin up a local test chain for you. Copy one of the mock private keys it provides you in the terminal, we'll need this to import the account into our MetaMask wallet. With this chain running, open a second terminal and run the command `make deploy`. This will compile and deploy our FundMe project onto our locally running blockchain. Assuming you've not run into errors. That's all that's required to set up the back end. -Return to Metamask, and within your network selector choose `Add Network`. +Return to MetaMask, and within your network selector choose `Add Network`. ::image{src='/html-fundme/2-metamask/metamask6.png' style='width: 75%; height: auto;'} @@ -144,17 +144,17 @@ In the subsequent page, inter your local network information as follows and clic Next, we need to add one of our `anvil` accounts to the wallet! -Click the account displayed at the top of your Metamask and select `Add an account or hardware wallet` from the bottom of the list. +Click the account displayed at the top of your MetaMask and select `Add an account or hardware wallet` from the bottom of the list. You'll be prompted to `add a new account`, `import an account`, or `add a hardware wallet`. Select `import an account` and enter your previously copied mock private key into the field provided. ::image{src='/html-fundme/2-metamask/metamask7.png' style='width: 75%; height: auto;'} -ALRIGHT. With all the set up done, we should be able to select our `anvil` chain in Metamask, then select the account we just added and click the `connect` button. +ALRIGHT. With all the set up done, we should be able to select our `anvil` chain in MetaMask, then select the account we just added and click the `connect` button. If we click `getBalance` we should have `0` returned in our console reflecting the balance of our deployed contract. At this point, we should be able to enter an amount and click `fund`. -Our Metamask pops up and has us sign the transaction, funding the contract with the amount we've entered! +Our MetaMask pops up and has us sign the transaction, funding the contract with the amount we've entered! ```js async function fund() { @@ -184,11 +184,11 @@ The function being called when we click this button is very similar in structure - define our `provider` - acquire the `signer` (account credentials) - define the contract/target of our call - - these are hardcoded for simplification purposes in this example and can be found in the [**constants.js**](https://github.com/Cyfrin/html-fund-me-f23/blob/main/constants.js) file of our [**html-fund-me repo**](https://github.com/Cyfrin/html-fund-me-f23). + - these are hardcoded for simplification purposes in this example and can be found in the [**constants.js**](https://github.com/Cyfrin/html-fund-me-cu/blob/main/constants.js) file of our [**html-fund-me repo**](https://github.com/Cyfrin/html-fund-me-cu). - submit transaction to the target contract with provided arguments. > **Note:** I'll stress again that this call being made by the front-end does **not** give the front-end access to private key data. The transaction is always sent to the wallet for confirmation/signing. ### Wrap Up -We've learnt a lot about how browser wallets like Metamask work under the hood and actually send our transactions to the blockchain. Great work - we've more low level concepts to cover in our next lesson. +We've learnt a lot about how browser wallets like MetaMask work under the hood and actually send our transactions to the blockchain. Great work - we've more low level concepts to cover in our next lesson. diff --git a/courses/foundry/3-html-fund-me/4-function-selectors/+page.md b/courses/foundry/3-html-fund-me/4-function-selectors/+page.md index 3e0b59d5..f45e611c 100644 --- a/courses/foundry/3-html-fund-me/4-function-selectors/+page.md +++ b/courses/foundry/3-html-fund-me/4-function-selectors/+page.md @@ -8,7 +8,7 @@ _Follow along the course with this video._ ### Intro to Function Selectors -Continuing from the last lesson, when we call the `fund` function our Metamask is going to pop up with a bunch of information about the transaction. +Continuing from the last lesson, when we call the `fund` function our MetaMask is going to pop up with a bunch of information about the transaction. ::image{src='/html-fundme/3-function-selector/function-selector1.png' style='width: 75%; height: auto;'} @@ -24,7 +24,7 @@ When we call our `fund` function, this is converted to a `function selector` tha cast sig "fund()" ``` -The above should result in the output `0xb60d4288` and when we compare this to the `Hex` data in our Metamask, we see that it does indeed match! +The above should result in the output `0xb60d4288` and when we compare this to the `Hex` data in our MetaMask, we see that it does indeed match! Were the function being called something secret/nefarious like `stealMoney()`. This function selector would be completely different. Running our cast command again confirms this clearly with a return of `0xa7ea5e4e`. @@ -43,7 +43,7 @@ function fund(uint256 amount) public payable { } ``` -If we were to call this function, the information Metamask gives us is a little different. +If we were to call this function, the information MetaMask gives us is a little different. ::image{src='/html-fundme/3-function-selector/function-selector3.png' style='width: 75%; height: auto;'} diff --git a/courses/security/3-first-audit/17-recommended-mitigation/+page.md b/courses/security/3-first-audit/17-recommended-mitigation/+page.md index 54a94609..dc300332 100644 --- a/courses/security/3-first-audit/17-recommended-mitigation/+page.md +++ b/courses/security/3-first-audit/17-recommended-mitigation/+page.md @@ -53,7 +53,6 @@ And get an output of: ---- ### Recommended Mitigation diff --git a/courses/security/3-first-audit/20-missing-access-controls-proof-of-code/+page.md b/courses/security/3-first-audit/20-missing-access-controls-proof-of-code/+page.md index cc109497..42b338e8 100644 --- a/courses/security/3-first-audit/20-missing-access-controls-proof-of-code/+page.md +++ b/courses/security/3-first-audit/20-missing-access-controls-proof-of-code/+page.md @@ -29,7 +29,6 @@ emit SetNewPassword(); ---- ### Proof of Concept/Proof of Code diff --git a/courses/security/3-first-audit/25-assesing-highs/+page.md b/courses/security/3-first-audit/25-assesing-highs/+page.md index 1feac358..95f45410 100644 --- a/courses/security/3-first-audit/25-assesing-highs/+page.md +++ b/courses/security/3-first-audit/25-assesing-highs/+page.md @@ -40,7 +40,6 @@ Alright! We're ready to start applying our understanding of `likelihood` and `im ---- Let's consider impacts and likelhoods of our first scenario (I've provided you a reference to them above). @@ -88,7 +87,6 @@ Applying our assessment to our finding title should look like this: ---- Considering our second finding, we can tell that anyone being able to set the password at any time is a severe disruption of protocol functionality. A clear `High` `Impact`. diff --git a/courses/security/3-first-audit/26-severity-rating-informational/+page.md b/courses/security/3-first-audit/26-severity-rating-informational/+page.md index 312de6b6..c3691f34 100644 --- a/courses/security/3-first-audit/26-severity-rating-informational/+page.md +++ b/courses/security/3-first-audit/26-severity-rating-informational/+page.md @@ -28,7 +28,6 @@ _Follow along with this video:_ ---- Just like before, let's ask ourselves things like diff --git a/courses/security/4-puppy-raffle/13-dos-poc/+page.md b/courses/security/4-puppy-raffle/13-dos-poc/+page.md index aea8cd85..ff363203 100644 --- a/courses/security/4-puppy-raffle/13-dos-poc/+page.md +++ b/courses/security/4-puppy-raffle/13-dos-poc/+page.md @@ -123,7 +123,6 @@ If we rerun our test we can see.. Our test passes! The second 100 players are pa ---- ### Wrap Up diff --git a/courses/security/4-puppy-raffle/14-dos-reporting/+page.md b/courses/security/4-puppy-raffle/14-dos-reporting/+page.md index 9a3b017a..a40cb2ca 100644 --- a/courses/security/4-puppy-raffle/14-dos-reporting/+page.md +++ b/courses/security/4-puppy-raffle/14-dos-reporting/+page.md @@ -225,6 +225,5 @@ function testDenialOfService() public { ---- Things look great! Lets finally have a look at what mitigations we can recommend for this vulnerability, in the next lesson. diff --git a/courses/security/4-puppy-raffle/21-reentrancy-menace-to-society/+page.md b/courses/security/4-puppy-raffle/21-reentrancy-menace-to-society/+page.md index 88f335f5..98c498a5 100644 --- a/courses/security/4-puppy-raffle/21-reentrancy-menace-to-society/+page.md +++ b/courses/security/4-puppy-raffle/21-reentrancy-menace-to-society/+page.md @@ -105,7 +105,6 @@ contract DAO is DAOInterface, Token, TokenCreation { ---- Hopefully we can spot the problem above. The DAO was making external calls before updating its state! diff --git a/courses/security/4-puppy-raffle/40-slither-walkthrough/+page.md b/courses/security/4-puppy-raffle/40-slither-walkthrough/+page.md index aa0131e0..e9f8a918 100644 --- a/courses/security/4-puppy-raffle/40-slither-walkthrough/+page.md +++ b/courses/security/4-puppy-raffle/40-slither-walkthrough/+page.md @@ -136,7 +136,6 @@ payable(msg.sender).sendValue(entranceFee); - `_tokenOwners.set(tokenId,to) (lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol#399)` ---- You can remove these warning from your `Slither` report by navigating to the respective lines for each call in the library and adding: @@ -327,7 +326,6 @@ We called this one out as an informational/gas finding as well. You can disable - `(success) = feeAddress.call{value: feesToWithdraw}() (src/PuppyRaffle.sol#167)` ---- Much like Assembly, `Slither` doesn't like low level calls. We'll be ignoring these for now, but you can remove them from your warnings by applying this line above the described calls. diff --git a/courses/security/4-puppy-raffle/5-tooling-aderyn/+page.md b/courses/security/4-puppy-raffle/5-tooling-aderyn/+page.md index 76ee0240..fc19101c 100644 --- a/courses/security/4-puppy-raffle/5-tooling-aderyn/+page.md +++ b/courses/security/4-puppy-raffle/5-tooling-aderyn/+page.md @@ -257,7 +257,6 @@ Index event fields make the field more quickly accessible to off-chain tools tha ---- _**WOW!**_ We have a report of vulnerabilities already gorgeously formatted and ready to be added to our audit report. diff --git a/courses/security/4-puppy-raffle/59-reporting-integer-overflow/+page.md b/courses/security/4-puppy-raffle/59-reporting-integer-overflow/+page.md index 53d945e6..046f069e 100644 --- a/courses/security/4-puppy-raffle/59-reporting-integer-overflow/+page.md +++ b/courses/security/4-puppy-raffle/59-reporting-integer-overflow/+page.md @@ -112,7 +112,6 @@ function testTotalFeesOverflow() public playersEntered { ---- I trust you attempted the PoC yourself - time to add our recommended mitigation diff --git a/courses/security/4-puppy-raffle/63-adding-the-audit-to-our-portfolio/+page.md b/courses/security/4-puppy-raffle/63-adding-the-audit-to-our-portfolio/+page.md index 04caada2..773628bf 100644 --- a/courses/security/4-puppy-raffle/63-adding-the-audit-to-our-portfolio/+page.md +++ b/courses/security/4-puppy-raffle/63-adding-the-audit-to-our-portfolio/+page.md @@ -777,7 +777,6 @@ View the report in the dropdown below, please know it's quiet long. ---- The final step, once the template has been filled out is to run our CLI command diff --git a/courses/security/4-puppy-raffle/7-recon-reading-docs/+page.md b/courses/security/4-puppy-raffle/7-recon-reading-docs/+page.md index bc45576a..eba55237 100644 --- a/courses/security/4-puppy-raffle/7-recon-reading-docs/+page.md +++ b/courses/security/4-puppy-raffle/7-recon-reading-docs/+page.md @@ -32,7 +32,6 @@ This project is to enter a raffle to win a cute dog NFT. The protocol should do ---- Above we see a pretty clear description of the protocol and it's intended functionality. What I like to do is open a `notes.md` file in my project and summarize things in my own words. diff --git a/courses/security/5-tswap/31-pool-factory/+page.md b/courses/security/5-tswap/31-pool-factory/+page.md index 46d3d025..e9583a4b 100644 --- a/courses/security/5-tswap/31-pool-factory/+page.md +++ b/courses/security/5-tswap/31-pool-factory/+page.md @@ -92,7 +92,6 @@ contract PoolFactory { ---- ### Imports and Errors diff --git a/courses/security/5-tswap/34-add-liquidity/+page.md b/courses/security/5-tswap/34-add-liquidity/+page.md index 00316601..5ae48b9c 100644 --- a/courses/security/5-tswap/34-add-liquidity/+page.md +++ b/courses/security/5-tswap/34-add-liquidity/+page.md @@ -65,7 +65,6 @@ function deposit( ---- Continuing down from `uint63 deadline`, we see a modifier we assessed earlier being applied - `revertIfZero(wethToDeposit)`. diff --git a/courses/security/5-tswap/35-remove-liquidity/+page.md b/courses/security/5-tswap/35-remove-liquidity/+page.md index 219d4347..89f3c2bd 100644 --- a/courses/security/5-tswap/35-remove-liquidity/+page.md +++ b/courses/security/5-tswap/35-remove-liquidity/+page.md @@ -52,7 +52,6 @@ function withdraw( ---- So, we know that liquidity providers are provided LP tokens in exchange for the liquidity they add to a pool - at a rate proportional to the percentage of the pool they've contributed. @@ -136,7 +135,6 @@ function getOutputAmountBasedOnInput( ---- I want to draw your attention to these lines - which of course we would flag for `magic numbers`: @@ -181,7 +179,6 @@ function getInputAmountBasedOnOutput( ---- Alright, there's only only line, a return calculation, but something should stick out to you here. Rather than using 1,000 in the fee calculation, the protocol has used 10,000! diff --git a/courses/security/5-tswap/36-exact-input/+page.md b/courses/security/5-tswap/36-exact-input/+page.md index 88a30644..e74999a5 100644 --- a/courses/security/5-tswap/36-exact-input/+page.md +++ b/courses/security/5-tswap/36-exact-input/+page.md @@ -55,7 +55,6 @@ function swapExactInput( ---- Let's better understand the parameters required by `swapExactInput`. diff --git a/courses/security/5-tswap/37-exact-output/+page.md b/courses/security/5-tswap/37-exact-output/+page.md index 526a5f4a..51af5f36 100644 --- a/courses/security/5-tswap/37-exact-output/+page.md +++ b/courses/security/5-tswap/37-exact-output/+page.md @@ -34,7 +34,6 @@ function swapExactOutput( ---- One thing that may stand out to us right away is that there doesn't seem to be any check on maximum input (and there's one fewer parameter as well), similar to how our `swapExactInput` function has the `minOutputAmount`, this difference definitely has our senses tingling. diff --git a/courses/security/5-tswap/38-sell-pool-tokens/+page.md b/courses/security/5-tswap/38-sell-pool-tokens/+page.md index 642e0cdf..83e5b74c 100644 --- a/courses/security/5-tswap/38-sell-pool-tokens/+page.md +++ b/courses/security/5-tswap/38-sell-pool-tokens/+page.md @@ -22,7 +22,6 @@ function sellPoolTokens(uint256 poolTokenAmount) external returns (uint256 wethA ---- This function just serves as a simplified wrapper for users to call `swapExactOutput` through. With any function call passing parameters, we should verify that what's being passed is correct as well as it's order. These are easy things for developers to overlook. diff --git a/courses/security/5-tswap/40-phase-4-reporting/+page.md b/courses/security/5-tswap/40-phase-4-reporting/+page.md index 794ced6b..66381d6d 100644 --- a/courses/security/5-tswap/40-phase-4-reporting/+page.md +++ b/courses/security/5-tswap/40-phase-4-reporting/+page.md @@ -23,7 +23,6 @@ Reference our [**finding_layout.md**](https://github.com/Cyfrin/security-and-aud ----
[I-2] `PoolFactory::constructor` Lacking zero address check @@ -41,7 +40,6 @@ constructor(address wethToken) {
----
[I-3] `PoolFactory::createPool should use .symbol() instead of .name() @@ -55,7 +53,6 @@ constructor(address wethToken) {
----
[I-4] `TSwapPool::constructor` Lacking zero address check - wethToken & poolToken @@ -81,7 +78,6 @@ constructor(
----
[I-5] `TSwapPool` events should be indexed @@ -95,7 +91,6 @@ constructor(
---- ### Wrap Up diff --git a/courses/security/5-tswap/41-missing-deadline-write-up/+page.md b/courses/security/5-tswap/41-missing-deadline-write-up/+page.md index b30966a6..4e5c556c 100644 --- a/courses/security/5-tswap/41-missing-deadline-write-up/+page.md +++ b/courses/security/5-tswap/41-missing-deadline-write-up/+page.md @@ -121,7 +121,6 @@ function deposit( ---- ### Wrap Up diff --git a/courses/security/5-tswap/42-reporting-continued/+page.md b/courses/security/5-tswap/42-reporting-continued/+page.md index 2954c2f7..02d63fc8 100644 --- a/courses/security/5-tswap/42-reporting-continued/+page.md +++ b/courses/security/5-tswap/42-reporting-continued/+page.md @@ -51,7 +51,6 @@ When it comes to auditing smart contracts, there are a lot of nitty-gritty detai ---- > **Note:** We omitted a PoC in our write up above. This vulnerability is pretty self-evident, but if you wanted to write one as a challenge, I encourage you to try! @@ -106,7 +105,6 @@ We identified a magic number resulting in an inaccurate fee caculation! This one ---- Attempting to write the `PoC` for the above is really important. Writing lots of `PoCs` is how you'll get better at them and `PoCs` are how you _prove_ there's an issue, often they help you _test_ if there's an issue. @@ -163,7 +161,6 @@ This will be our second low, compare your write up to mine below: ---- ### Wrap Up diff --git a/courses/security/5-tswap/43-no-slippage-protection/+page.md b/courses/security/5-tswap/43-no-slippage-protection/+page.md index 2609f0a0..2eb9b9fd 100644 --- a/courses/security/5-tswap/43-no-slippage-protection/+page.md +++ b/courses/security/5-tswap/43-no-slippage-protection/+page.md @@ -78,7 +78,6 @@ Let's write up our second `high severity` finding! Compare your write up to mine ---- ### Wrap Up diff --git a/courses/security/5-tswap/44-sell-pool-tokens-write-up/+page.md b/courses/security/5-tswap/44-sell-pool-tokens-write-up/+page.md index 9df7f7e5..81c03bc6 100644 --- a/courses/security/5-tswap/44-sell-pool-tokens-write-up/+page.md +++ b/courses/security/5-tswap/44-sell-pool-tokens-write-up/+page.md @@ -62,7 +62,6 @@ Additionally, it might be wise to add a deadline to the function, as there is cu ---- ### Wrap Up diff --git a/courses/security/5-tswap/45-invariant-break-write-up-and-poc/+page.md b/courses/security/5-tswap/45-invariant-break-write-up-and-poc/+page.md index d37e54cd..112ef9a2 100644 --- a/courses/security/5-tswap/45-invariant-break-write-up-and-poc/+page.md +++ b/courses/security/5-tswap/45-invariant-break-write-up-and-poc/+page.md @@ -67,7 +67,6 @@ function _swap(IERC20 inputToken, uint256 inputAmount, IERC20 outputToken, uint2 ---- Alright, we can begin by giving the test a unique name. It looks like the first half of this function is handling deposits to add liquidity to the pool, we can keep that in. @@ -271,7 +270,6 @@ Place the following into `TSwapPool.t.sol`. ---- ### Wrap Up diff --git a/courses/security/5-tswap/47-creating-pdf-for-your-portfolio/+page.md b/courses/security/5-tswap/47-creating-pdf-for-your-portfolio/+page.md index 70de9daf..e2db5048 100644 --- a/courses/security/5-tswap/47-creating-pdf-for-your-portfolio/+page.md +++ b/courses/security/5-tswap/47-creating-pdf-for-your-portfolio/+page.md @@ -393,7 +393,6 @@ I've included a copy of my completed template here for reference if you get stuc ---- Once your report template is filled out, we just have to run `pandoc` on it! diff --git a/courses/security/6-thunder-loan/12-exploit-centralization/+page.md b/courses/security/6-thunder-loan/12-exploit-centralization/+page.md index d52384c3..386a0381 100644 --- a/courses/security/6-thunder-loan/12-exploit-centralization/+page.md +++ b/courses/security/6-thunder-loan/12-exploit-centralization/+page.md @@ -285,7 +285,6 @@ Consider removing empty blocks. ---- The first vulnerability identified is `## L-1: Centralization Risk for trusted owners`. diff --git a/courses/security/6-thunder-loan/14-static-analysis-continued/+page.md b/courses/security/6-thunder-loan/14-static-analysis-continued/+page.md index 11c16944..aa3fa79f 100644 --- a/courses/security/6-thunder-loan/14-static-analysis-continued/+page.md +++ b/courses/security/6-thunder-loan/14-static-analysis-continued/+page.md @@ -282,7 +282,6 @@ Consider removing empty blocks. ---- The remainder of detections by `Aderyn` are going to qualify as gas/informational findings. As such we'll likely just copy these outputs into our audit report - this is a huge time saving benefit of `Aderyn`. diff --git a/courses/security/6-thunder-loan/19-oracle-upgradeable/+page.md b/courses/security/6-thunder-loan/19-oracle-upgradeable/+page.md index 313b82e3..cde58d7f 100644 --- a/courses/security/6-thunder-loan/19-oracle-upgradeable/+page.md +++ b/courses/security/6-thunder-loan/19-oracle-upgradeable/+page.md @@ -48,7 +48,6 @@ contract OracleUpgradeable is Initializable { ---- Starting from the top, we can verify that our 3 imports `ITSwapPool`, `IPoolFactory` and `Initializable` are being utilized, and they are. diff --git a/courses/security/6-thunder-loan/21-failure-to-initialize-remix/+page.md b/courses/security/6-thunder-loan/21-failure-to-initialize-remix/+page.md index f5d62170..7544382d 100644 --- a/courses/security/6-thunder-loan/21-failure-to-initialize-remix/+page.md +++ b/courses/security/6-thunder-loan/21-failure-to-initialize-remix/+page.md @@ -35,7 +35,6 @@ contract FailureToInitialize is Initializable { ---- The example here is very simple, but it should illustrate the potential impact of failing to initialize. Go ahead and compile and deploy `FailureToInitialize.sol` diff --git a/courses/security/6-thunder-loan/23-oracleupgradeable-continued/+page.md b/courses/security/6-thunder-loan/23-oracleupgradeable-continued/+page.md index d4b19e73..ac93115e 100644 --- a/courses/security/6-thunder-loan/23-oracleupgradeable-continued/+page.md +++ b/courses/security/6-thunder-loan/23-oracleupgradeable-continued/+page.md @@ -46,7 +46,6 @@ contract OracleUpgradeable is Initializable { ---- ```js function getPriceInWeth(address token) public view returns (uint256) { diff --git a/courses/security/6-thunder-loan/24-assettoken/+page.md b/courses/security/6-thunder-loan/24-assettoken/+page.md index a47c5bd2..e6111920 100644 --- a/courses/security/6-thunder-loan/24-assettoken/+page.md +++ b/courses/security/6-thunder-loan/24-assettoken/+page.md @@ -124,7 +124,6 @@ contract AssetToken is ERC20 { ---- We should recall from the README: diff --git a/courses/security/6-thunder-loan/26-thunderloan-starting-at-the-top/+page.md b/courses/security/6-thunder-loan/26-thunderloan-starting-at-the-top/+page.md index 703cb8cd..99ce5645 100644 --- a/courses/security/6-thunder-loan/26-thunderloan-starting-at-the-top/+page.md +++ b/courses/security/6-thunder-loan/26-thunderloan-starting-at-the-top/+page.md @@ -307,7 +307,6 @@ contract ThunderLoan is Initializable, OwnableUpgradeable, UUPSUpgradeable, Orac ---- > **Remember:** The intent here is a first pass, we're not going to try to go too deep right away. Often it's important to prime your brain with content before getting deep into review. diff --git a/courses/security/6-thunder-loan/27-thunderloan-functions/+page.md b/courses/security/6-thunder-loan/27-thunderloan-functions/+page.md index bb35a88b..40d9f802 100644 --- a/courses/security/6-thunder-loan/27-thunderloan-functions/+page.md +++ b/courses/security/6-thunder-loan/27-thunderloan-functions/+page.md @@ -305,7 +305,6 @@ contract ThunderLoan is Initializable, OwnableUpgradeable, UUPSUpgradeable, Orac ---- Continuing with our review of the ThunderLoan.sol contract, the very next bit of code we approach may seem unfamiliar. diff --git a/courses/security/6-thunder-loan/28-testing-deleting-mappings/+page.md b/courses/security/6-thunder-loan/28-testing-deleting-mappings/+page.md index 9f843684..2d5c3e42 100644 --- a/courses/security/6-thunder-loan/28-testing-deleting-mappings/+page.md +++ b/courses/security/6-thunder-loan/28-testing-deleting-mappings/+page.md @@ -60,7 +60,6 @@ contract DeleteMappingTest { ---- If we deploy the above contract in Remix we can then set two addresses as `token` and `token2` to see the mapping working as expected: diff --git a/courses/security/6-thunder-loan/30-thunderloan-continued/+page.md b/courses/security/6-thunder-loan/30-thunderloan-continued/+page.md index bc6f1251..70473539 100644 --- a/courses/security/6-thunder-loan/30-thunderloan-continued/+page.md +++ b/courses/security/6-thunder-loan/30-thunderloan-continued/+page.md @@ -24,7 +24,6 @@ function deposit(IERC20 token, uint256 amount) external revertIfZero(amount) rev ---- The first thing the `deposit` function does is leverage the `s_tokenToAssetToken` mapping to acquire the `AssetToken` paired with the passed token parameter. Remember, these `asset tokens` ultimately represent how much of the pool the depositer owns as a result of their deposits. diff --git a/courses/security/6-thunder-loan/32-thunderloan-redeem/+page.md b/courses/security/6-thunder-loan/32-thunderloan-redeem/+page.md index 86739540..8fcfdec5 100644 --- a/courses/security/6-thunder-loan/32-thunderloan-redeem/+page.md +++ b/courses/security/6-thunder-loan/32-thunderloan-redeem/+page.md @@ -35,7 +35,6 @@ function redeem( ---- We would expect this function to behave in opposite to the `deposit` function. Thankfully we have `NATSPEC` this time to verify the function's intent! diff --git a/courses/security/6-thunder-loan/33-thunderloan-flashloan/+page.md b/courses/security/6-thunder-loan/33-thunderloan-flashloan/+page.md index 2b4a33cf..44f11ab2 100644 --- a/courses/security/6-thunder-loan/33-thunderloan-flashloan/+page.md +++ b/courses/security/6-thunder-loan/33-thunderloan-flashloan/+page.md @@ -56,7 +56,6 @@ function flashloan( ---- ```js // @Audit-Informational: No NATSPEC! @@ -240,7 +239,6 @@ function functionCallWithValue(address target, bytes memory data, uint256 value) ---- We can see, in the end of the chain of executions, we're hitting a very classic piece of code. @@ -333,7 +331,6 @@ contract MockFlashLoanReceiver { ---- Let's consider this example implementation of executeOperation for a better idea of how funds are handled once borrowed: diff --git a/courses/security/6-thunder-loan/34-note-on-being-discouraged/+page.md b/courses/security/6-thunder-loan/34-note-on-being-discouraged/+page.md index f39e5bcd..d8d1fe52 100644 --- a/courses/security/6-thunder-loan/34-note-on-being-discouraged/+page.md +++ b/courses/security/6-thunder-loan/34-note-on-being-discouraged/+page.md @@ -15,7 +15,6 @@ _Follow along with the video lesson:_ ---- Having just gone through one of the major functions in Thunder Loan, and still finding nothing juicy, we may start to feel discouraged again. diff --git a/courses/security/6-thunder-loan/37-improving-test-coverage-to-find-a-high/+page.md b/courses/security/6-thunder-loan/37-improving-test-coverage-to-find-a-high/+page.md index b89e1123..916d534e 100644 --- a/courses/security/6-thunder-loan/37-improving-test-coverage-to-find-a-high/+page.md +++ b/courses/security/6-thunder-loan/37-improving-test-coverage-to-find-a-high/+page.md @@ -121,7 +121,6 @@ contract ThunderLoanTest is BaseTest { ---- This seems like a good place to start as it will weed out any issues that may arise from the issue we identified in `deposit` outlined above. @@ -227,7 +226,6 @@ contract MockFlashLoanReceiver { ---- Ok, now we know that the exchange rate is being updated when `deposit` is called (in this case in our modifier), as well as when `flashloan` is called. Let's add a call to redeem into our test, at this point we'd expect a liquidity provider to be able to redeem their asset tokens in exchange for underlying. diff --git a/courses/security/6-thunder-loan/40-exploit-oracle-manipulation-thunderloan-poc/+page.md b/courses/security/6-thunder-loan/40-exploit-oracle-manipulation-thunderloan-poc/+page.md index 547de0c1..c09c1389 100644 --- a/courses/security/6-thunder-loan/40-exploit-oracle-manipulation-thunderloan-poc/+page.md +++ b/courses/security/6-thunder-loan/40-exploit-oracle-manipulation-thunderloan-poc/+page.md @@ -117,7 +117,6 @@ function testOracleManipulation() public { ---- Next, let's add the funding for ThunderLoan to our test. Before we're able to do that, if you recall from earlier, ThunderLoan needs to have allowed our tokens. @@ -330,7 +329,6 @@ contract MaliciousFlashLoanReceiver is IFlashLoanReceiver { ---- ### Back to our Test @@ -420,7 +418,6 @@ function testOracleManipulation() public { ---- Let's run our test command and you'll see why. @@ -610,7 +607,6 @@ contract MaliciousFlashLoanReceiver is IFlashLoanReceiver { ---- **Recommended Mitigation:** Consider using a different price oracle mechanism, like a Chainlink price feed with a Uniswap TWAP fallback oracle. diff --git a/courses/security/6-thunder-loan/41-oracle-manipulation-recap/+page.md b/courses/security/6-thunder-loan/41-oracle-manipulation-recap/+page.md index 0aab5fe3..e60a8ba8 100644 --- a/courses/security/6-thunder-loan/41-oracle-manipulation-recap/+page.md +++ b/courses/security/6-thunder-loan/41-oracle-manipulation-recap/+page.md @@ -170,7 +170,6 @@ contract MaliciousFlashLoanReceiver is IFlashLoanReceiver { ---- ::image{src='/security-section-6/41-oracle-manipulation-recap/oracle-manipulation-recap3.png' style='width: 100%; height: auto;'} diff --git a/courses/security/6-thunder-loan/45-exploit-storage-collision-remix-examplee/+page.md b/courses/security/6-thunder-loan/45-exploit-storage-collision-remix-examplee/+page.md index 78c0864c..caa00e1a 100644 --- a/courses/security/6-thunder-loan/45-exploit-storage-collision-remix-examplee/+page.md +++ b/courses/security/6-thunder-loan/45-exploit-storage-collision-remix-examplee/+page.md @@ -78,7 +78,6 @@ contract ImplementationB { ---- Of note, the `initialized` value will typically default to `0` or `false`, if any number is assigned to this variable, it's going to return `true`. diff --git a/courses/security/7-bridges/11-bossbridge/+page.md b/courses/security/7-bridges/11-bossbridge/+page.md index 8b9b33a7..086fa4da 100644 --- a/courses/security/7-bridges/11-bossbridge/+page.md +++ b/courses/security/7-bridges/11-bossbridge/+page.md @@ -144,7 +144,6 @@ contract L1BossBridge is Ownable, Pausable, ReentrancyGuard { ---- There's actually not _a lot_ of code here, but it's arguably _pretty tough_ code. Let's jump in. diff --git a/courses/security/7-bridges/12-signatures/+page.md b/courses/security/7-bridges/12-signatures/+page.md index 463d9724..f56422d8 100644 --- a/courses/security/7-bridges/12-signatures/+page.md +++ b/courses/security/7-bridges/12-signatures/+page.md @@ -104,7 +104,6 @@ library MessageHashUtils { ---- ```js /* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. diff --git a/courses/security/7-bridges/14-eip-712/+page.md b/courses/security/7-bridges/14-eip-712/+page.md index 3a3bd7d6..2131a041 100644 --- a/courses/security/7-bridges/14-eip-712/+page.md +++ b/courses/security/7-bridges/14-eip-712/+page.md @@ -121,7 +121,6 @@ contract Example { ---- The test function at the bottom of the contract shows how everything ultimately comes together. diff --git a/courses/security/7-bridges/20-arbitrary-poc/+page.md b/courses/security/7-bridges/20-arbitrary-poc/+page.md index 47fe2088..d0e88ecf 100644 --- a/courses/security/7-bridges/20-arbitrary-poc/+page.md +++ b/courses/security/7-bridges/20-arbitrary-poc/+page.md @@ -74,7 +74,6 @@ function testCanMoveApprovedTokensOfOtherUsers() public { ---- Now, we can run this test. diff --git a/courses/security/7-bridges/5-Docs/+page.md b/courses/security/7-bridges/5-Docs/+page.md index bf452407..dec18aa1 100644 --- a/courses/security/7-bridges/5-Docs/+page.md +++ b/courses/security/7-bridges/5-Docs/+page.md @@ -38,7 +38,6 @@ The bridge operator is in charge of signing withdrawal requests submitted by use ---- The docs seem thorough, but unless we have experience with bridges, or similar protocols this is pretty confusing. This would be the point in a private audit that I would ask for some `protocol diagrams`. diff --git a/courses/security/8-mev-and-governance/5-mev-in-puppy-raffle/+page.md b/courses/security/8-mev-and-governance/5-mev-in-puppy-raffle/+page.md index a273f25f..c36569a1 100644 --- a/courses/security/8-mev-and-governance/5-mev-in-puppy-raffle/+page.md +++ b/courses/security/8-mev-and-governance/5-mev-in-puppy-raffle/+page.md @@ -48,7 +48,6 @@ function selectWinner() external { ---- Effectively, when the `selectWinner` function is called, the transaction is then sent to the MemPool. At this point anyone can see the results of the selectWinner function. If a user participating in the raffle identifies that they didn't win, the potential exists for them to refund their entry fee! diff --git a/courses/security/8-mev-and-governance/6-mev-t-swap/+page.md b/courses/security/8-mev-and-governance/6-mev-t-swap/+page.md index 764a7eb4..08ba13f6 100644 --- a/courses/security/8-mev-and-governance/6-mev-t-swap/+page.md +++ b/courses/security/8-mev-and-governance/6-mev-t-swap/+page.md @@ -92,7 +92,6 @@ function deposit( ---- We identified, during our review, that the `deadline` parameter wasn't being used. How would that potentially lead to an `MEV` attack in `TSwap`? diff --git a/courses/solidity/1-simple-storage/10-mappings/+page.md b/courses/solidity/1-simple-storage/10-mappings/+page.md index c826d066..da1d9d4a 100644 --- a/courses/solidity/1-simple-storage/10-mappings/+page.md +++ b/courses/solidity/1-simple-storage/10-mappings/+page.md @@ -6,7 +6,7 @@ _You can follow along with the video course from here._ ### Introduction -We have just created a contract that stores multiple `Person`'s' names and favorite numbers in a list. In this session, you will learn about mappings, their functionality, and when it is more advantageous to use them. +We have just created a contract that stores multiple `Person`'s names and favorite numbers in a list. In this session, you will learn about mappings, their functionality, and when it is more advantageous to use them. ### Avoiding Costly Iterations @@ -18,9 +18,8 @@ list_of_people.add(Person("John", 8)); list_of_people.add(Person("Mariah", 10)); list_of_people.add(Person("Chelsea", 232)); -/* go through all the people to check their favorite number. -If name is "Chelsea" -> return 232 -*/ +// Go through all the people to check their favorite number. +// If name is "Chelsea" -> return 232 ``` Iterating through a long list of data is usually expensive and time-consuming, especially when we do not need to access elements by their index. @@ -42,10 +41,10 @@ nameToFavoriteNumber[_name] = _favoriteNumber; ``` > 👀❗**IMPORTANT**:br -> Mappings have a constant time complexity for lookups, meaning that retrieving a value by its key is done in constant time, +> Mappings have a constant time complexity for lookups, meaning that retrieving a value by its key is done in constant time. > 🗒️ **NOTE**:br -> The default value for all key types is zero. In our case, `nameToFavoriteNumber["ET"]` will be equal to 0. +> The default value for all key types is zero. In our case, `nameToFavoriteNumber["ET"]` equals 0. ### Conclusion diff --git a/courses/solidity/1-simple-storage/11-deploying/+page.md b/courses/solidity/1-simple-storage/11-deploying/+page.md index 391140bd..afda54f4 100644 --- a/courses/solidity/1-simple-storage/11-deploying/+page.md +++ b/courses/solidity/1-simple-storage/11-deploying/+page.md @@ -9,9 +9,9 @@ _You can follow along with the video course from here._ Over the past eight lessons, we crafted the `SimpleStorage` contract. It defines a custom type `Person`, includes an internal variable that can be read and updated, and contains a public array and mapping that can also be modified. In this lesson, we will deploy the contract to a **real testnet**, which fully simulates a live blockchain environment without using real Ether. > 🔥 **CAUTION**:br -> You could be tempted to immediately deploy this contract to a testnet. As a general rule, I caution against this. Make sure to write tests, carry out audits and ensure the robustness of your contract before deploying it to production. However, for the sake of this demonstration, we're going to deploy this as a dummy contract on a testnet. +> You could be tempted to immediately deploy this contract to the mainnet. As a general rule, I caution against this. Make sure to write tests, carry out audits and ensure the robustness of your contract before deploying it to production. However, for the sake of this demonstration, we're going to deploy this as a dummy contract on a testnet. -Before deploying, be always sure to make a **compilation check**. This ensures that the contract has no errors or warnings and is fit for deployment. +Before deploying, be always sure to make a **compilation check**. This ensures that the contract has no errors or warnings, and is fit for deployment. ### Deployment on a testnet @@ -37,9 +37,9 @@ Since the contract has been deployed, we can now interact with it and **update t > 👀❗**IMPORTANT**:br > View and pure functions will not send transactions -> 💡 **TIP**:br > _Celebrate small victories and milestones. These psychological boosts will keep you engaged in the learning process._ +> 💡 **TIP**:br _Celebrate small victories and milestones. These psychological boosts will keep you engaged in the learning process._ -It's possible to deploy a contract to different testnets or a real mainnet, just by switching the Metamask network. Be sure to have enough net-compatible ETHs to deploy your contract. +It's possible to deploy a contract to different testnets or a real mainnet, just by switching the MetaMask network. Be sure to have enough net-compatible ETHs to deploy your contract. ### Conclusion diff --git a/courses/solidity/1-simple-storage/12-zksync-deploying/+page.md b/courses/solidity/1-simple-storage/12-zksync-deploying/+page.md index 370493f9..75ee1ca7 100644 --- a/courses/solidity/1-simple-storage/12-zksync-deploying/+page.md +++ b/courses/solidity/1-simple-storage/12-zksync-deploying/+page.md @@ -8,7 +8,7 @@ _Follow along with the video_ ### Introduction -With the rising costs of deploying to the Ethereum mainnet, scaling solutions like roll-ups and Layer 2 networks are increasingly in demand. The following lessons will guide you through deploying a smart contract to L2 zkSync. If you've followed the zkSync lessons on the **Blockchain Basics** section, you should have already added ZK-Sync to your Metamask. If not, we'll walk through that process in the next lesson. +With the rising costs of deploying to the Ethereum mainnet, scaling solutions like roll-ups and Layer 2 networks are increasingly in demand. The following lessons will guide you through deploying a smart contract to L2 zkSync. If you've followed the zkSync lessons on the **Blockchain Basics** section, you should have already added ZKsync to your MetaMask. If not, we'll walk through that process in the next lesson. ### Testnet Funds @@ -19,4 +19,4 @@ To deploy contracts on zkSync, you'll need testnet funds. There are two methods ### Deploying -To deploy a contract to ZK-Sync, there are a few key steps to follow. First, it's crucial to configure zkSync in your Metamask wallet. Next, you'll need to acquire testnet ETH, which is necessary for deploying and testing your contract. +To deploy a contract to ZKsync, there are a few key steps to follow. First, it's crucial to configure zkSync in your MetaMask wallet. Next, you'll need to acquire testnet ETH, which is necessary for deploying and testing your contract. diff --git a/courses/solidity/1-simple-storage/18-evm-recap/+page.md b/courses/solidity/1-simple-storage/18-evm-recap/+page.md index 3fcdf11a..ae38d8d4 100644 --- a/courses/solidity/1-simple-storage/18-evm-recap/+page.md +++ b/courses/solidity/1-simple-storage/18-evm-recap/+page.md @@ -11,10 +11,10 @@ In this section, we'll quickly summarize the lessons from 1 to 9 and learn about ### EVM EVM stands for _Ethereum Virtual Machine_. It's a decentralized computational engine that executes smart contracts. -Any contract that it's written in Solidity, can be deployed to any EVM-compatible blockchain. Examples of such blockchains and Layer 2 solutions include **Ethereum**, **Polygon**, **Arbitram**, **Optimism**, and **Zksync**. +Any contract that it's written in Solidity, can be deployed to any EVM-compatible blockchain. Examples of such blockchains and Layer 2 solutions include **Ethereum**, **Polygon**, **Arbitrum**, **Optimism**, and **ZKsync**. > 🚧 **WARNING**:br -> Although a blockchain like Zksync may be EVM-compatible, it is essential to verify that all Solidity keywords are supported +> Although a blockchain like ZKsync may be EVM-compatible, it is essential to verify that all Solidity keywords are supported ### Contract Setup diff --git a/courses/solidity/1-simple-storage/2-best-practices/+page.md b/courses/solidity/1-simple-storage/2-best-practices/+page.md index 1456ca5a..ef116406 100644 --- a/courses/solidity/1-simple-storage/2-best-practices/+page.md +++ b/courses/solidity/1-simple-storage/2-best-practices/+page.md @@ -16,7 +16,7 @@ In the top right corner of the lesson view, you'll find a link to the [**GitHub Next to the video lesson, there is a **written lesson** tab 📝. This tab provides the course content in text format, which is useful for reading along, copy-pasting code snippets or reviewing updates. -If you're watching on YouTube, links to these resources are available in the video description. However, watching this course on Cyphrin Updraft offers additional benefits like tracking your progress and accessing written lessons, which will enhance your learning experience. +If you're watching on YouTube, links to these resources are available in the video description. However, watching this course on Cyfrin Updraft offers additional benefits like tracking your progress and accessing written lessons, which will enhance your learning experience. ### Best Practices for Learning diff --git a/courses/solidity/1-simple-storage/3-introduction/+page.md b/courses/solidity/1-simple-storage/3-introduction/+page.md index defe11ea..c08317df 100644 --- a/courses/solidity/1-simple-storage/3-introduction/+page.md +++ b/courses/solidity/1-simple-storage/3-introduction/+page.md @@ -28,7 +28,7 @@ The repository serves two main purposes: #### Asking Questions -It’s very likely that during your journey you’ll have questions. It’s recommended to use the [**Q&A**](https://github.com/Cyfrin/foundry-full-course-f23/discussions/new?category=q-a) section provided inside the discussion tab. You will be guided into how to best formulate your dubts and queries, such that have the highest chance of being answered by the community, AI or a forum. +It’s very likely that during your journey you’ll have questions. It’s recommended to use the [**Q&A**](https://github.com/Cyfrin/foundry-full-course-f23/discussions/new?category=q-a) section provided inside the discussion tab. You will be guided into how to best formulate your doubts and queries, such that have the highest chance of being answered by the community, AI or a forum. ### Setting Up diff --git a/courses/solidity/1-simple-storage/4-setting-up-your-first-contract/+page.md b/courses/solidity/1-simple-storage/4-setting-up-your-first-contract/+page.md index dc2a8346..178c89c3 100644 --- a/courses/solidity/1-simple-storage/4-setting-up-your-first-contract/+page.md +++ b/courses/solidity/1-simple-storage/4-setting-up-your-first-contract/+page.md @@ -85,6 +85,6 @@ Well done! You just created and compiled your first smart contract in Solidity. ### 🧑‍💻 Test yourself 1. 📕 What does IDE mean and what are Remix main features? -2. 📕 What's the keywork `pragma` used for? +2. 📕 What's the keyword `pragma` used for? 3. 📕 Explain what compiling a contract means. 4. 🧑‍💻 Write an empty contract that contains a SPDX License Identifier and compiles with version 0.8.11 or 0.8.13. diff --git a/courses/solidity/1-simple-storage/5-basic-types/+page.md b/courses/solidity/1-simple-storage/5-basic-types/+page.md index a1a2808e..c2846c96 100644 --- a/courses/solidity/1-simple-storage/5-basic-types/+page.md +++ b/courses/solidity/1-simple-storage/5-basic-types/+page.md @@ -15,7 +15,7 @@ Solidity supports various _elementary_ types that can be combined to create more - Boolean (bool): true or false - Unsigned Integer (uint): unsigned whole number (positive) - Integer (int): signed whole number (positive and negative) -- Address (address): 20 bytes value. An example of an address can be found within your Metamask account. +- Address (address): 20 bytes value. An example of an address can be found within your MetaMask account. - Bytes (bytes): low-level raw byte data ### Variables definition @@ -23,7 +23,7 @@ Solidity supports various _elementary_ types that can be combined to create more Variables are just placeholders for **values**. A value can be one **data type** described in the list above. For instance, we could create a Boolean variable named `hasFavoriteNumber`, which would represent whether someone has a favourite number or not (constant `true` or `false`). ```solidity -bool hasFavoriteNumber = true; //the variable hasFavoriteNumber` represents the value `true` +bool hasFavoriteNumber = true; // The variable `hasFavoriteNumber` represents the value `true` ``` It's possible to specify the number of **bits** used for `uint` and `int`. For example, uint256 specifies that the variable has 256 bits. uint is a shorthand for uint256. @@ -37,7 +37,7 @@ The _semicolon_ at the end of each line signifies that a statement is completed. // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -contract Simple Storage{ +contract SimpleStorage { // Basic types bool hasFavoriteNumber = true; uint256 favoriteNumber = 88; @@ -60,7 +60,7 @@ bytes dynamicBytes = "I am a dynamic array, so you can manipulate my size"; Bytes can be allocated in size (up to `bytes32`). However, bytes and bytes32 represent distinct data types. -**Strings** are internally represented as _dynamic byte arrays_ (`bytes` tipe) and designed specifically for working with text. For this reason, a string can easily be converted into bytes. +**Strings** are internally represented as _dynamic byte arrays_ (`bytes` type) and designed specifically for working with text. For this reason, a string can easily be converted into bytes. [Bits and Bytes overview](https://www.youtube.com/watch?v=Dnd28lQHquU) diff --git a/courses/solidity/1-simple-storage/6-functions/+page.md b/courses/solidity/1-simple-storage/6-functions/+page.md index 78b92fc4..d716e68e 100644 --- a/courses/solidity/1-simple-storage/6-functions/+page.md +++ b/courses/solidity/1-simple-storage/6-functions/+page.md @@ -64,7 +64,7 @@ If we open the Remix terminal we can see that deploying the contract has just se Let's send a transaction to the `store` function to change the value of the variable `favoriteNumber`: you can insert a number and press the `store` button in Remix. A transaction is initiated and after some time, its status will change from pending to complete. -💸 From the accounts section, it becomes visible that ETH is being consumed every time a transaction is submitted. When the state of the blockchain is modified (e.g. deploying a contract, sending ETH, ..), is done by sending a transaction that consumes **gas**. Executing the `store` function is more expensive than just transferring ETH between accounts, with the rising gas expenses primarily associated (though not exclusively) with the code length. +💸 From the accounts section, it becomes visible that ETH is being consumed every time a transaction is submitted. When the state of the blockchain is modified (e.g. deploying a contract, sending ETH, ...), is done by sending a transaction that consumes **gas**. Executing the `store` function is more expensive than just transferring ETH between accounts, with the rising gas expenses primarily associated (though not exclusively) with the code length. #### Verifying the stored value @@ -73,7 +73,7 @@ This contract is missing a way to check if the number has been updated: now we c The default visibility of the `favoriteNumber` variable is **internal**, preventing external contracts and users from viewing it. > 🗒️ **NOTE**:br -> Appending the `public` keyword next to a variable will automatically change its visibility and it will generate a **get function**. +> Appending the `public` keyword next to a variable will automatically change its visibility and it will generate a **getter function** (a function that gets the variable's value when called). ```solidity uint256 public favoriteNumber; @@ -99,13 +99,13 @@ If a visibility specifier is not given, it defaults to `internal`. The terms `view` and `pure` are used when a function reads values from the blockchain without altering its state. Such functions will not initiate transactions but rather make calls, represented as blue buttons in the Remix interface. A `pure` function will prohibit any reading from the state or storage. ```solidity -function retrieve() public view returns(uint256){ +function retrieve() public view returns(uint256) { return favoriteNumber; } ``` ```solidity -function retrieve() public pure returns(uint256){ +function retrieve() public pure returns(uint256) { return 7; } ``` diff --git a/courses/solidity/1-simple-storage/7-arrays-and-structs/+page.md b/courses/solidity/1-simple-storage/7-arrays-and-structs/+page.md index 65020396..75ae48b7 100644 --- a/courses/solidity/1-simple-storage/7-arrays-and-structs/+page.md +++ b/courses/solidity/1-simple-storage/7-arrays-and-structs/+page.md @@ -6,7 +6,7 @@ _You can follow along with the video course from here._ ### Introduction -Up to this point, the `SimpleStorage` contract allows for storing, updating, and viewing a single favorite number. In this lesson, we'll enhance the code to store multiple numbers, enabling more than one person to store their values. We'll learn how to create a list of favorite numbers using **arrays**, and we'll explore the **`structs`** keyword for creating new types in Solidity. +Up to this point, the `SimpleStorage` contract allows for storing, updating, and viewing a single favorite number. In this lesson, we'll enhance the code to store multiple numbers, enabling more than one person to store their values. We'll learn how to create a list of favorite numbers using **arrays**, and we'll explore the **`struct`** keyword for creating new types in Solidity. ### Arrays and struct @@ -23,7 +23,7 @@ uint256[] list_of_favorite_numbers = [0, 78, 90]; ``` > 🗒️ **NOTE**:br -> Arrays are zero-indexed: the first element is at position zero (0), the second is position (index) 1, and so on. +> Arrays are zero-indexed: the first element is at position zero (has index 0), the second element is at position one (has index 1), and so on. The issue with this data structure is that we cannot link the owner with its favorite value. One solution is to establish a **new type** using the `struct` keyword, named `Person`, which is made of two _attributes_: a favorite number and a name. @@ -43,7 +43,7 @@ From this struct, we can instantiate a variable `my_friend` that has the type `P Person public my_friend = Person(7, 'Pat'); /* equals to Person public my_friend = Person({ - favoriteNumber:7, + favorite_number:7, name:'Pat'}); */ ``` diff --git a/courses/solidity/1-simple-storage/8-errors-and-warnings/+page.md b/courses/solidity/1-simple-storage/8-errors-and-warnings/+page.md index 0592e722..8557de09 100644 --- a/courses/solidity/1-simple-storage/8-errors-and-warnings/+page.md +++ b/courses/solidity/1-simple-storage/8-errors-and-warnings/+page.md @@ -45,7 +45,7 @@ We can input the compiler error under the drop-down menu, execute the search, an ::image{src='/solidity/remix/lesson-2/errors-warnings/phind-answer.png' style='width: 100%; height: auto;'} -#### Othe resources +#### Other resources It is advised to make active use of AI tools, as they can substantially boost your understanding and skills. Later in this course, we will explore how to ask effective questions, utilize AI prompts, structure your inquiries, and improve your search and learning techniques. diff --git a/courses/solidity/1-simple-storage/9-memory-storage-calldata/+page.md b/courses/solidity/1-simple-storage/9-memory-storage-calldata/+page.md index 420a7078..8796895b 100644 --- a/courses/solidity/1-simple-storage/9-memory-storage-calldata/+page.md +++ b/courses/solidity/1-simple-storage/9-memory-storage-calldata/+page.md @@ -53,7 +53,7 @@ In our contract, the variable `myFavoriteNumber` is a storage variable. Variable ```solidity contract MyContract { - uint256 favoriteNumber; //this is a storage variable + uint256 favoriteNumber; // this is a storage variable }; ``` @@ -73,7 +73,7 @@ In Solidity, a `string` is recognized as an **array of bytes**. On the other han > You can't use the `storage` keyword for variables inside a function. Only `memory` and `calldata` are allowed here, as the variable only exists temporarily. ```solidity -function addPerson(string memory _name, uitn256 _favoriteNumber) public { //cannot use storage as input parameters +function addPerson(string memory _name, uitn256 _favoriteNumber) public { // cannot use storage as input parameters uint256 test = 0; // variable here can be stored in memory or stack listOfPeople.push(Person(_favoriteNumber, _name)); } diff --git a/courses/solidity/2-storage-factory/1-factory-introduction/+page.md b/courses/solidity/2-storage-factory/1-factory-introduction/+page.md index 8657e4fa..2e3d56ac 100644 --- a/courses/solidity/2-storage-factory/1-factory-introduction/+page.md +++ b/courses/solidity/2-storage-factory/1-factory-introduction/+page.md @@ -6,7 +6,7 @@ _You can follow along with the video course from here._ ### Introduction -You can find the code for this section in the [Remix Storage Factory Github repository](https://github.com/cyfrin/remix-storage-factory-f23). In these nine lessons we'll work with three new contracts: +You can find the code for this section in the [Remix Storage Factory Github repository](https://github.com/cyfrin/remix-storage-factory-f23). In these nine lessons, we'll work with three new contracts: 1. `SimpleStorage.sol` - the contract we build in the previous section, with some modifications 2. `AddFiveStorage.sol` - a child contract of `SimpleStorage` that leverages _inheritance_ @@ -30,8 +30,6 @@ It's possible to interact with this newly deployed `SimpleStorage` via the `stor The **`sfGet`** function, when given the input '0', will indeed return the number provided by the previous function. The **address** of the `SimpleStorage` contract can then be retrieved by clicking on the get function `listOfSimpleStorageContracts`. -::image{src='/solidity/remix/lesson-3/setting-up/graph-1.png' style='width: 100%; height: auto;'} - ### Conclusion The `StorageFactory` contract manages numerous instances of an external contract `SimpleStorage`. It provides functionality to deploy new contract instances dynamically and allows for the storage and retrieval of values from each instance. These instances are maintained and organized within an array, enabling efficient tracking and interaction. diff --git a/courses/solidity/2-storage-factory/2-setting-up-the-factory/+page.md b/courses/solidity/2-storage-factory/2-setting-up-the-factory/+page.md index 99b2c040..3cb233bd 100644 --- a/courses/solidity/2-storage-factory/2-setting-up-the-factory/+page.md +++ b/courses/solidity/2-storage-factory/2-setting-up-the-factory/+page.md @@ -10,7 +10,7 @@ In this `StorageFactory` setup, we'll explore what _composability_ means, showin ### StorageFactory setup -You can begin by visiting the [Github repository of the previous section](https://github.com/cyfrin/remix-simple-storage-f23) and copying the contract `SimpleStorage` inside Remix. +You can begin by visiting the [Github repository of the previous section](https://github.com/cyfrin/remix-simple-storage-cu) and copying the contract `SimpleStorage` inside Remix. This contract allows to store a favorite number, a list of people with their favorite number, a mapping and different functionalities to interact with them. This lesson aims to create a **new contract** that can deploy and interact with `SimpleStorage`. @@ -26,7 +26,7 @@ pragma solidity ^0.8.18; contract StorageFactory { function createSimplestorageContract() public { - //how does StorageFactory know what SimpleStorage looks like? + // How does StorageFactory know what SimpleStorage looks like? } } ``` diff --git a/courses/solidity/2-storage-factory/3-deploying-a-contract-from-a-contract/+page.md b/courses/solidity/2-storage-factory/3-deploying-a-contract-from-a-contract/+page.md index f8df48af..f9da88fe 100644 --- a/courses/solidity/2-storage-factory/3-deploying-a-contract-from-a-contract/+page.md +++ b/courses/solidity/2-storage-factory/3-deploying-a-contract-from-a-contract/+page.md @@ -25,7 +25,8 @@ contract StorageFactory { } ``` -> 👀❗**IMPORTANT**:br > `SimpleStorage` on the left and `simpleStorage` on the right are treated as entirely distinct entities due to their differing capitalization. `Simple Storage` refers to the contract type while `simpleStorage` refers to the variable name. +> 👀❗**IMPORTANT**:br +> `SimpleStorage` on the left and `simpleStorage` on the right are treated as entirely distinct entities due to their differing capitalization. `Simple Storage` refers to the contract type while `simpleStorage` refers to the variable name. When the `new` keyword is used, the compiler recognizes the intention to deploy a new contract instance. After compiling, we can proceed to deploy it. diff --git a/courses/solidity/2-storage-factory/5-ai-help-ii/+page.md b/courses/solidity/2-storage-factory/5-ai-help-ii/+page.md index 7ea8dfa4..ae2fce5a 100644 --- a/courses/solidity/2-storage-factory/5-ai-help-ii/+page.md +++ b/courses/solidity/2-storage-factory/5-ai-help-ii/+page.md @@ -25,7 +25,7 @@ We can ask the AI to clarify it by: This is the AI prompt: -```` +````text Hi, I'm having a hard time understanding the difference between these simple storages on this line: ```// paste the confusing line of code here``` Here is my full code: diff --git a/courses/solidity/2-storage-factory/7-inheritance-in-solidity/+page.md b/courses/solidity/2-storage-factory/7-inheritance-in-solidity/+page.md index 42bdca20..f294c465 100644 --- a/courses/solidity/2-storage-factory/7-inheritance-in-solidity/+page.md +++ b/courses/solidity/2-storage-factory/7-inheritance-in-solidity/+page.md @@ -11,7 +11,7 @@ In this lesson, we are going to introduce the concept of **inheritance** and **o ### Inheritance We are going to enhance the `SimpleStorage` contract by adding a new functionality: the ability to add five (5) to the stored `favoriteNumber`. -To achieve this, we could duplicate the existing `SimpleStorage` contract and make changes to the new version. However, this approach leads to redundant code. A better practice could be to utilize **inheritance**, wich is the mechanism that allows the `AddFiveStorage` contract to derive all the functionalities of `SimpleStorage`. +To achieve this, we could duplicate the existing `SimpleStorage` contract and make changes to the new version. However, this approach leads to redundant code. A better practice could be to utilize **inheritance**, which is the mechanism that allows the `AddFiveStorage` contract to derive all the functionalities of `SimpleStorage`. Let's begin by creating a new file `AddFiveStorage.sol` and name-importing `SimpleStorage.sol`: diff --git a/courses/solidity/3-fund-me/1-fund-me-intro/+page.md b/courses/solidity/3-fund-me/1-fund-me-intro/+page.md index 6e78e813..4e20130f 100644 --- a/courses/solidity/3-fund-me/1-fund-me-intro/+page.md +++ b/courses/solidity/3-fund-me/1-fund-me-intro/+page.md @@ -19,7 +19,7 @@ For this project, we will be using two contracts: `FundMe`, the main crowdfundin Once `FundMe` is deployed on Remix, you'll notice a set of _functions_, including a new red button labelled `fund`, indicating that the function is _payable_. A payable function allows you to send native blockchain currency (e.g., Ethereum, Polygon, Avalanche) to the contract. -We'll additionally indicate a **minimum USD amount** to send to the contract when the function `fund` is called. To transfer funds to the `FundMe` contract, you can navigate to the _value section_ of the Remix deployment tab, enter a value (e.g. 0.1 ether) then hit `fund`. A Metamask transaction confirmation will appear, and the contract balance will remain zero until the transaction is finalized. Once completed, the contract balance will be updated to reflect the transferred amount. +We'll additionally indicate a **minimum USD amount** to send to the contract when the function `fund` is called. To transfer funds to the `FundMe` contract, you can navigate to the _value section_ of the Remix deployment tab, enter a value (e.g. 0.1 ether) then hit `fund`. A MetaMask transaction confirmation will appear, and the contract balance will remain zero until the transaction is finalized. Once completed, the contract balance will be updated to reflect the transferred amount. The contract owner can then `withdraw` the funds. In this case, since we own the contract, the balance will be removed from the contract's balance and transferred to our wallet. diff --git a/courses/solidity/3-fund-me/10-getting-prices-from-chainlink/+page.md b/courses/solidity/3-fund-me/10-getting-prices-from-chainlink/+page.md index f2f11fb0..f8aa6d05 100644 --- a/courses/solidity/3-fund-me/10-getting-prices-from-chainlink/+page.md +++ b/courses/solidity/3-fund-me/10-getting-prices-from-chainlink/+page.md @@ -18,13 +18,13 @@ AggregatorV3Interface priceFeed = AggregatorV3Interface(0x1b44F3514812d835EB1BDB We can call the `latestRoundData()` function on this interface to obtain several values, including the latest price. -```js +```solidity function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); ``` For now, we'll focus on the `answer` value and ignore the other returned values by using commas as placeholders for the unneeded variables. -```js +```solidity function getLatestPrice() public view returns (int) { (,int price,,,) = priceFeed.latestRoundData(); return price; @@ -48,13 +48,13 @@ return price * 1e10; Typecasting, or type conversion, involves converting a value from one data type to another. In Solidity, not all data types can be converted due to differences in their underlying representations and the potential for data loss. However, certain conversions, such as from `int` to `uint`, are allowed. -```js +```solidity return uint(price) * 1e10; ``` We can finalize our `view` function as follows: -```js +```solidity function getLatestPrice() public view returns (uint256) { (,int answer,,,) = priceFeed.latestRoundData(); return uint(answer) * 1e10; diff --git a/courses/solidity/3-fund-me/11-more-solidity-math/+page.md b/courses/solidity/3-fund-me/11-more-solidity-math/+page.md index 0d9f16fc..53e8d256 100644 --- a/courses/solidity/3-fund-me/11-more-solidity-math/+page.md +++ b/courses/solidity/3-fund-me/11-more-solidity-math/+page.md @@ -35,7 +35,7 @@ function getConversionRate(uint256 ethAmount) internal view returns (uint256) { - `ethAmount` is set at 1 ETH, with 1e18 precision. - `ethPrice` is set at 2000 USD, with 1e18 precision, resulting in 2000e18. -- `ethPrice * ethAmount` results in 2000e18. +- `ethPrice * ethAmount` results in 2000e36. - To scale down `ethAmountInUsd` to 1e18 precision, divide `ethPrice * ethAmount` by 1e18. ### Checking Minimum USD Value diff --git a/courses/solidity/3-fund-me/14-libraries/+page.md b/courses/solidity/3-fund-me/14-libraries/+page.md index b92ebdd7..c2134f26 100644 --- a/courses/solidity/3-fund-me/14-libraries/+page.md +++ b/courses/solidity/3-fund-me/14-libraries/+page.md @@ -26,23 +26,23 @@ library PriceConverter {} Let's copy `getPrice`, `getConversionRate`, and `getVersion` functions from the `FundMe.sol` contract into our new library, remembering to import the `AggregatorV3Interface` into `PriceConverter.sol`. Finally, we can mark all the functions as `internal`. -```js +```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol"; library PriceConverter { - function getPrice() internal view returns (uint256) { - AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306); - (, int256 answer, , , ) = priceFeed.latestRoundData(); - return uint256(answer * 10000000000); - } - - function getConversionRate(uint256 ethAmount) internal view returns (uint256) { - uint256 ethPrice = getPrice(); - uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000; - return ethAmountInUsd; + function getPrice() internal view returns (uint256) { + AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306); + (, int256 answer, , , ) = priceFeed.latestRoundData(); + return uint256(answer * 10000000000); + } + + function getConversionRate(uint256 ethAmount) internal view returns (uint256) { + uint256 ethPrice = getPrice(); + uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1000000000000000000; + return ethAmountInUsd; } } ``` @@ -51,20 +51,20 @@ library PriceConverter { You can import the library in your contract and attach it to the desired type with the keyword `using`: -```js +```solidity import {PriceConverter} from "./PriceConverter.sol"; using PriceConverter for uint256; ``` The `PriceConverter` functions can then be called as if they are native to the `uint256` type. For example, calling the `getConversionRate()` function will now be changed into: -```js +```solidity require(msg.value.getConversionRate() >= minimumUsd, "didn't send enough ETH"); ``` Here, `msg.value`, which is a `uint256` type, is extended to include the `getConversionRate()` function. The `msg.value` gets passed as the first argument to the function. If additional arguments are needed, they are passed in parentheses: -```js +```solidity uint256 result = msg.value.getConversionRate(123); ``` diff --git a/courses/solidity/3-fund-me/15-safemath/+page.md b/courses/solidity/3-fund-me/15-safemath/+page.md index 34027141..bda65701 100644 --- a/courses/solidity/3-fund-me/15-safemath/+page.md +++ b/courses/solidity/3-fund-me/15-safemath/+page.md @@ -14,7 +14,7 @@ In this lesson, we will explore `SafeMath`, a widely used library before Solidit Let's begin by creating a new file called `SafeMathTester.sol` and adding a function `add` that increments the `bigNumber` state variable. -```js +```solidity // SafeMathTester.sol pragma solidity ^0.6.0; @@ -22,7 +22,7 @@ contract SafeMathTester { uint8 public bigNumber = 255; function add() public { - bigNumber = bigNumber + 1; + bigNumber = bigNumber + 1; } } ``` @@ -35,7 +35,7 @@ Before Solidity version **0.8.0**, signed and unsigned integers were **unchecked `SafeMath.sol` provided a mechanism to revert transactions when the maximum limit of a `uint256` data type was reached. It was a typical security measure across contracts to avoid erroneous calculations and potential exploits. -```js +```solidity function add(uint a, uint b) public pure returns (uint) { uint c = a + b; require(c >= a, "SafeMath: addition overflow"); @@ -49,13 +49,13 @@ With the introduction of Solidity version 0.8, automatic checks for overflows an For scenarios where mathematical operations are known not to exceed a variable's limit, Solidity introduced the `unchecked` construct to make code more _gas-efficient_. Wrapping the addition operation with `unchecked` will _ignore the overflow and underflow checks_: if the `bigNumber` exceeds the limit, it will wrap its value to zero. -```js +```solidity uint8 public bigNumber = 255; function add() public { unchecked { - bigNumber = bigNumber + 1; - } + bigNumber = bigNumber + 1; + } } ``` @@ -70,6 +70,6 @@ The evolution of Solidity and `SafeMath.sol` highlights the continuous advanceme 1. 📕 Why was the `SafeMath` library widely used before version 0.8? 2. 📕 Explain the meaning of integer overflow and integer underflow. Make an example using `uint16`. -3. 📕 What happened after solidity version 0.8? +3. 📕 What happened after Solidity version 0.8? 4. 📕 What is the unchecked construct? 5. 🧑‍💻 Modify the `SafeMathTester` contract by using the SafeMath library to prevent integer overflow. diff --git a/courses/solidity/3-fund-me/18-sending-eth-from-a-contract/+page.md b/courses/solidity/3-fund-me/18-sending-eth-from-a-contract/+page.md index 8f83a026..7717fdbf 100644 --- a/courses/solidity/3-fund-me/18-sending-eth-from-a-contract/+page.md +++ b/courses/solidity/3-fund-me/18-sending-eth-from-a-contract/+page.md @@ -12,7 +12,7 @@ This lesson explores three different methods of sending ETH from one account to The `transfer` function is the simplest way to send Ether to a recipient address: -```js +```solidity payable(msg.sender).transfer(amount); // the current contract sends the Ether amount to the msg.sender ``` @@ -24,7 +24,7 @@ However, `transfer` has a significant limitation. It can only use up to 2300 gas The `send` function is similar to `transfer`, but it differs in its behaviour: -```js +```solidity bool success = payable(msg.sender).send(address(this).balance); require(success, "Send failed"); ``` @@ -35,7 +35,7 @@ Like `transfer`, `send` also has a gas limit of 2300. If the gas limit is reache The `call` function is flexible and powerful. It can be used to call any function **without requiring its ABI**. It does not have a gas limit, and like `send`, it returns a boolean value instead of reverting like `transfer`. -```js +```solidity (bool success, ) = payable(msg.sender).call{value: address(this).balance}(""); require(success, "Call failed"); ``` @@ -44,7 +44,8 @@ To send funds using the `call` function, we convert the address of the receiver The `call` function returns two variables: a boolean for success or failure, and a byte object which stores returned data if any. -> 👀❗**IMPORTANT**:br > `call` is the recommended way of sending and receiving Ethereum or other blockchain native tokens. +> 👀❗**IMPORTANT**:br +> `call` is the recommended way of sending and receiving Ethereum or other blockchain native tokens. ### Conclusion diff --git a/courses/solidity/3-fund-me/19-constructor/+page.md b/courses/solidity/3-fund-me/19-constructor/+page.md index c77c2065..11f69eb7 100644 --- a/courses/solidity/3-fund-me/19-constructor/+page.md +++ b/courses/solidity/3-fund-me/19-constructor/+page.md @@ -16,7 +16,7 @@ One solution could be to create a function, `callMeRightAway`, to assign the rol A more efficient solution is to use a **constructor** function: -```js +```solidity constructor() {} ``` @@ -29,7 +29,7 @@ The constructor function is automatically called during contract deployment, wit We can use the constructor to set the contract's owner immediately after deployment: -```js +```solidity address public owner; constructor() { owner = msg.sender; @@ -42,14 +42,14 @@ Here, we initialize the state variable `owner` with the contract deployer's addr The next step is to update the `withdraw` function to ensure it can only be called by the owner: -```js +```solidity function withdraw() public { require(msg.sender == owner, "must be owner"); - // rest of the function here + // rest of the function here } ``` -Before executing any withdrawal actions, we check that `msg.sender` is the owner. If the caller is not the owner, the operation **reverts** with the error message "must be the owner." This access restriction ensures that only the intended account can execute the function. +Before executing any withdrawal actions, we check that `msg.sender` is the owner. If the caller is not the owner, the operation **reverts** with the error message "must be the owner" This access restriction ensures that only the intended account can execute the function. ### Conclusion diff --git a/courses/solidity/3-fund-me/2-setup/+page.md b/courses/solidity/3-fund-me/2-setup/+page.md index 0d2ebed1..93ca9ff3 100644 --- a/courses/solidity/3-fund-me/2-setup/+page.md +++ b/courses/solidity/3-fund-me/2-setup/+page.md @@ -38,7 +38,7 @@ The FundMe contract will have two primary functions that serve as the main inter First, let's code the `fund` function and leave the `withdraw` function commented out for the moment. -```js +```solidity contract FundMe { // send funds into our contract function fund() public {} diff --git a/courses/solidity/3-fund-me/20-modifiers/+page.md b/courses/solidity/3-fund-me/20-modifiers/+page.md index df64acd0..fbc331f8 100644 --- a/courses/solidity/3-fund-me/20-modifiers/+page.md +++ b/courses/solidity/3-fund-me/20-modifiers/+page.md @@ -12,7 +12,7 @@ In this lesson, we will explore **modifiers** and how they can simplify code wri If we build a contract with multiple _administrative functions_, that should only be executed by the contract owner, we might repeatedly check the caller identity: -```js +```solidity require(msg.sender == owner, "Sender is not owner"); ``` @@ -24,7 +24,7 @@ Modifiers in Solidity allow embedding **custom lines of code** within any functi Here's how to create a modifier: -```js +```solidity modifier onlyOwner { require(msg.sender == owner, "Sender is not owner"); _; @@ -40,7 +40,7 @@ The underscore `_` placed in the body is a placeholder for the modified function For example, the `onlyOwner` modifier can be applied to the `withdraw` function like this: -```js +```solidity function withdraw(uint amount) public onlyOwner { // Function logic } diff --git a/courses/solidity/3-fund-me/23-custom-errors/+page.md b/courses/solidity/3-fund-me/23-custom-errors/+page.md index eb1a82a7..f9acf9c8 100644 --- a/courses/solidity/3-fund-me/23-custom-errors/+page.md +++ b/courses/solidity/3-fund-me/23-custom-errors/+page.md @@ -18,15 +18,15 @@ Introduced in **Solidity 0.8.4**, custom errors can be used in `revert` statemen We can start by creating a custom error: -```js +```solidity error NotOwner(); ``` Then, we can replace the `require` function with an `if` statement, using the `revert` function with the newly created error: -```js +```solidity if (msg.sender != i_owner) { - revert NotOwner(); + revert NotOwner(); } ``` diff --git a/courses/solidity/3-fund-me/24-receive-fallback/+page.md b/courses/solidity/3-fund-me/24-receive-fallback/+page.md index 09edeaf9..0eafba46 100644 --- a/courses/solidity/3-fund-me/24-receive-fallback/+page.md +++ b/courses/solidity/3-fund-me/24-receive-fallback/+page.md @@ -8,13 +8,13 @@ _You can follow along with the video course from here._ In Solidity, if Ether is sent to a contract without a `receive` or `fallback` function, the transaction will be **rejected**, and the Ether will not be transferred. In this lesson, we'll explore how to handle this scenario effectively. -### receiv and fallback functions +### receive and fallback functions `receive` and `fallback` are _special functions_ triggered when users send Ether directly to the contract or call non-existent functions. These functions do not return anything and must be declared `external`. To illustrate, let's create a simple contract: -```js +```solidity //SPDX-License-Identifier: MIT pragma solidity ^0.8.7; @@ -33,22 +33,24 @@ contract FallbackExample { In this contract, `result` is initialized to zero. When Ether is sent to the contract, the `receive` function is triggered, setting `result` to one. If a transaction includes **data** but the specified function _does not exist_, the `fallback` function will be triggered, setting `result` to two. For a comprehensive explanation, refer to [SolidityByExample](https://solidity-by-example.org/fallback/). -// Ether is sent to the contract -// is msg.data empty? -// / \ -// yes no -// / \ -// receive() ? fallback() -// / \ -// yes no -// / \ -// receive() fallback() +```text +// Ether is sent to contract +// is msg.data empty? +// / \ +// yes no +// / \ +// receive()? fallback() +// / \ +// yes no +// / \ +//receive() fallback() +``` ### Sending Ether to fundMe When a user sends Ether **directly** to the `fundMe` contract without calling the `fund` function, the `receive` function can be used to _redirect_ the transaction to the `fund` function: -```js +```solidity receive() external payable { fund(); } diff --git a/courses/solidity/3-fund-me/25-zksync-plugin-fix/+page.md b/courses/solidity/3-fund-me/25-zksync-plugin-fix/+page.md index e3af272f..ba0b91f0 100644 --- a/courses/solidity/3-fund-me/25-zksync-plugin-fix/+page.md +++ b/courses/solidity/3-fund-me/25-zksync-plugin-fix/+page.md @@ -1,13 +1,13 @@ --- -title: zkSync Plugin Fix +title: ZKsync Plugin Fix --- _Follow along with the video_ --- -### zkSync Remix plugin minor bug +### ZKsync Remix plugin minor bug -As we saw in the _Simple Storage_ section, there is a small bug in the Remix zkSync module. After a successful compilation, the deploy tab will still display the message _`no smart contracts ready for deployment`_. +As we saw in the _Simple Storage_ section, there is a small bug in the Remix ZKsync module. After a successful compilation, the deploy tab will still display the message _`no smart contracts ready for deployment`_. This issue arises due to a small bug in the plugin, which requires your smart contracts to be inside a **`contracts` folder**. To resolve this, you can create a new folder named 'contracts' and move your smart contract into it. You can then proceed to compile the contract again, and you should be able to deploy it without any issues. diff --git a/courses/solidity/3-fund-me/26-deploying-to-zksync/+page.md b/courses/solidity/3-fund-me/26-deploying-to-zksync/+page.md index daf871c7..93a030b1 100644 --- a/courses/solidity/3-fund-me/26-deploying-to-zksync/+page.md +++ b/courses/solidity/3-fund-me/26-deploying-to-zksync/+page.md @@ -1,5 +1,5 @@ --- -title: deploying FundMe to zkSync +title: deploying FundMe to ZKsync --- _Follow along with the video_ @@ -8,21 +8,21 @@ _Follow along with the video_ ### Introduction -In this lesson, we'll walk through the steps to deploy the `FundMe` contract to the zkSync testnet. +In this lesson, we'll walk through the steps to deploy the `FundMe` contract to the ZKsync testnet. -### Adjustments for zkSync +### Adjustments for ZKsync -First, we'll need the specific the correct **price feed address** for the skSync Sepolia chain, which you can find in the [Chainlink documentation](https://docs.chain.link/data-feeds/price-feeds/addresses?network=zksync&page=1). Each chain has its own unique addresses, and the ETH/USD address for the zkSync Sepolia testnet will differ from the one on Sepolia. +First, we'll need the specific the correct **price feed address** for the ZKsync Sepolia chain, which you can find in the [Chainlink documentation](https://docs.chain.link/data-feeds/price-feeds/addresses?network=zksync&page=1). Each chain has its own unique addresses, and the ETH/USD address for the ZKsync Sepolia testnet will differ from the one on Sepolia. -Then copy the zkSync Sepolia testnet ETH/USD address and replace the existing address in the `FundMe::getVersion` and `PriceConverter::getPrice` functions: +Then, copy the ZKsync Sepolia testnet ETH/USD address and replace the existing address in the `FundMe::getVersion` and `PriceConverter::getPrice` functions: -```js -AggregatorV3Interface priceFeed = AggregatorV3Interface(0xfEefF7c3fB57d18C5C6Cdd71e45D2D0b4F9377bF); // Add ETH/USD zkSync Sepolia address here +```solidity +AggregatorV3Interface priceFeed = AggregatorV3Interface(0xfEefF7c3fB57d18C5C6Cdd71e45D2D0b4F9377bF); // Add ETH/USD ZKsync Sepolia address here ``` -Since the zkSync plugin may not handle libraries well yet, you can copy the library code directly into your contract instead of importing it. +Since the ZKsync plugin may not handle libraries well yet, you can copy the library code directly into your contract instead of importing it. -It's recommended using the correct version of the Solidity compiler by updating it to version `0.8.24` for zkSync compatibility. +It's recommended using the correct version of the Solidity compiler by updating it to version `0.8.24` for ZKsync compatibility. ### Deploying the Contract @@ -30,4 +30,4 @@ In the Environment tab, you can connect your MetaMask wallet and then **deploy a ### Conclusion -Deploying the `FundMe` contract to the zkSync testnet involves a few key steps. First, adjust the price feed addresses and handle the library code correctly and make sure you're using the correct version of the Solidity compiler. Then you are ready to connect the MetaMask wallet to remix zkSync module and deploy the contract. Finally, you can verify that everything is working as expected by calling the contract functions in the [zkSync block explorer](https://sepolia.explorer.zksync.io/). +Deploying the `FundMe` contract to the ZKsync testnet involves a few key steps. First, adjust the price feed addresses and handle the library code correctly and make sure you're using the correct version of the Solidity compiler. Then you are ready to connect the MetaMask wallet to Remix ZKsync module and deploy the contract. Finally, you can verify that everything is working as expected by calling the contract functions in the [ZKsync block explorer](https://sepolia.explorer.zksync.io/). diff --git a/courses/solidity/3-fund-me/27-recap-congratulations/+page.md b/courses/solidity/3-fund-me/27-recap-congratulations/+page.md index 544f89e9..995598c4 100644 --- a/courses/solidity/3-fund-me/27-recap-congratulations/+page.md +++ b/courses/solidity/3-fund-me/27-recap-congratulations/+page.md @@ -14,7 +14,7 @@ We have encountered the special functions `receive`, `fallback`, and `constructo To save gas, Solidity provides keywords like `constant` and `immutable` for variables that can only be set once: -```js +```solidity uint constant minimumUSD = 50 * 1e18; ``` diff --git a/courses/solidity/3-fund-me/5-real-world-price-data/+page.md b/courses/solidity/3-fund-me/5-real-world-price-data/+page.md index 120b778d..5ca9ca3e 100644 --- a/courses/solidity/3-fund-me/5-real-world-price-data/+page.md +++ b/courses/solidity/3-fund-me/5-real-world-price-data/+page.md @@ -32,7 +32,7 @@ Chainlink offers ready-made features that can be added to a smart contract. And - **Data Feeds** - **Verifiable Random Number** -- **Keepers** +- **Automation (previously known as "Keepers")** - **Functions** ### Chainlink Data Feeds @@ -44,13 +44,13 @@ _Chainlink Data Feeds_ are responsible for powering over $50 billion in the DeFi They aggregate this data and deliver it to a reference contract, the **price feed contract**, in a single transaction. Each contract will store the pricing details of a specific cryptocurrency ::image{src='/solidity/remix/lesson-4/datafeeds/datafeed1.png' style='width: 100%; height: auto;'} -### Chainlink CRF +### Chainlink VRF The Chainlink VRF (Verifiable Random Function) provides a solution for generating **provably random numbers**, ensuring true fairness in applications such as NFT randomization, lotteries, and gaming. These numbers are determined off-chain, and they are immune to manipulation. ::image{src='/solidity/remix/lesson-4/datafeeds/datafeed3.png' style='width: 100%; height: auto;'} -### Chainlink Keepers +### Chainlink Automation (previously known as "Keepers") Another great feature is Chainlink's system of _Keepers_. These **nodes** listen for specific events and, upon being triggered, automatically execute the intended actions within the calling contract. diff --git a/courses/solidity/3-fund-me/6-mid-lesson-recap/+page.md b/courses/solidity/3-fund-me/6-mid-lesson-recap/+page.md index 2c01f79a..42ff09ab 100644 --- a/courses/solidity/3-fund-me/6-mid-lesson-recap/+page.md +++ b/courses/solidity/3-fund-me/6-mid-lesson-recap/+page.md @@ -12,19 +12,19 @@ From lessons 1 to 5 we've explored the usage of the keyword `payable`, the globa To enable a function to receive a native blockchain token such as Ethereum, it must be marked as `payable`: -```js +```solidity function deposit() public payable { - balances[msg.sender] += msg.value; + balances[msg.sender] += msg.value; } ``` If we want a function to fail under certain conditions, we can use the `require` statement. For example, in a bank transfer scenario, we want the operation to fail if the sender does not have enough balance. Here's an example: -```js +```solidity function transfer(address recipient, uint amount) public { - require(balances[msg.sender] >= amount); - balances[msg.sender] -= amount; - balances[recipient] += amount; + require(balances[msg.sender] >= amount); + balances[msg.sender] -= amount; + balances[recipient] += amount; } ``` @@ -41,11 +41,11 @@ Chainlink is a revolutionary technology that enables the integration of external Consider a smart contract that deals with a commodity like gold. _Chainlink Price Feeds_ can provide real-time gold prices, allowing the smart contract to reflect the current market prices. -```js +```solidity import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; contract GoldPriceContract { AggregatorV3Interface internal priceFeed; - //The Chainlink price feed contract address + // The Chainlink price feed contract address constructor() public { priceFeed = AggregatorV3Interface(0x8468b2bDCE073A157E560AA4D9CcF6dB1DB98507); } diff --git a/courses/solidity/3-fund-me/7-interfaces/+page.md b/courses/solidity/3-fund-me/7-interfaces/+page.md index 775ef207..0a5978f7 100644 --- a/courses/solidity/3-fund-me/7-interfaces/+page.md +++ b/courses/solidity/3-fund-me/7-interfaces/+page.md @@ -12,7 +12,7 @@ In this part, we'll learn how to **convert** Ethereum (ETH) into Dollars (USD) a We begin by trying to convert the `msg.value`, which is now specified in ETH, into USD. This process requires fetching the **current USD market price** of Ethereum and using it to convert the `msg.value` amount into USD. -```js +```solidity // Function to get the price of Ethereum in USD function getPrice() public {} // Function to convert a value based on the price @@ -39,10 +39,10 @@ An alternative method involves the use of an **Interface**, which defines method We can test the Interface usage by calling the `version()` function: -```js - function getVersion() public view returns (uint256) { +```solidity +function getVersion() public view returns (uint256) { return AggregatorV3Interface(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419).version(); - } +} ``` > 🗒️ **NOTE**:br diff --git a/courses/solidity/3-fund-me/8-ai-help-iii/+page.md b/courses/solidity/3-fund-me/8-ai-help-iii/+page.md index 1da6c8f9..a3a206bb 100644 --- a/courses/solidity/3-fund-me/8-ai-help-iii/+page.md +++ b/courses/solidity/3-fund-me/8-ai-help-iii/+page.md @@ -59,4 +59,4 @@ By leveraging AI and discussion forums, you can gain a deeper understanding of c ### 🧑‍💻 Test yourself -1. 📕 Dive deeper into the `getVersion` function by asking AI three more questions about it +1. 📕 Delve deeper into the `getVersion` function by asking AI three more questions about it diff --git a/courses/solidity/3-fund-me/9-importing-from-npm-github/+page.md b/courses/solidity/3-fund-me/9-importing-from-npm-github/+page.md index 9efbed4b..1719b6e2 100644 --- a/courses/solidity/3-fund-me/9-importing-from-npm-github/+page.md +++ b/courses/solidity/3-fund-me/9-importing-from-npm-github/+page.md @@ -12,7 +12,7 @@ As we delve into smart contract development, **interacting** with external smart Let's take a look at the `SmartContract` interface as an example: -```js +```solidity interface SmartContract { function someFunction() external view returns(uint, uint){}; } @@ -26,13 +26,13 @@ Smart Contracts _hosted on GitHub_ can be imported directly into your project. F Instead of manually copying all its code into your project and then importing it like this: -```js +```solidity import { AggregatorV3Interface } from "./AggregatorV3Interface.sol"; ``` we can import it more efficiently, as specified in the [Chainlink documentation](https://docs.chain.link/docs/using-chainlink-reference-contracts): -```js +```solidity import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; ``` @@ -44,7 +44,7 @@ The `@chainlink/contracts` package, available on NPM, follows **Semantic Version Remix interprets `@chainlink/contracts` as a reference to the [NPM package](https://www.npmjs.com/package/@chainlink/contracts), and downloads all the necessary code from it. -```js +```solidity pragma solidity ^0.8.18; import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; contract FundMe {}