Skip to content

Commit

Permalink
Merge pull request #278 from Cyfrin/dev
Browse files Browse the repository at this point in the history
dev --> main
  • Loading branch information
solhosty authored Dec 12, 2024
2 parents dec6769 + e0f7a72 commit 3d2694f
Show file tree
Hide file tree
Showing 343 changed files with 7,118 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ In a default transaction, data is stored and visible on-chain permanently. Blob

### EIP4844

These transactions originate from EIP4844, also known as "Proto-DankSharding", introduced in the Dancun upgrade in March 2024. The purpose of this transaction type is to address Ethereum's high transaction costs.
These transactions originate from EIP4844, also known as "Proto-Danksharding", introduced in the Dencun upgrade in March 2024. The purpose of this transaction type is to address Ethereum's high transaction costs.

Roll-ups help scale Ethereum by executing multiple transactions on their own chains, compressing them into batches, and submitting these batches back to Ethereum. Prior to the upgrade, all compressed transaction data had to be permanently stored on Ethereum nodes, which was inefficient and costly. With EIP4844, the data can be submitted **temporarily** for validation, avoiding permanent storage.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,17 @@ With this setup, we can correctly encode and hash the `MESSAGE_TYPEHASH`, accoun
Finally, implement the `_isValidSignature` function:

```js
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

function _isValidSignature(
address signer,
address account,
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (bool) {
(address actualSigner,,) = ECDSA.tryRecover(digest, v, r, s);
return (actualSigner == signer);
return (actualSigner == account);
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The `signMessage` function will calculate the **message digest**, which will be

```js
function signMessage(uint256 privKey, address account) public view returns (uint8 v, bytes32 r, bytes32 s) {
bytes32 hashedMessage = airdrop.getMessageHash(account, amountToCollect);
bytes32 hashedMessage = airdrop.getMessageHash(account, AMOUNT_TO_CLAIM);
(v, r, s) = vm.sign(privKey, hashedMessage);
}
```
Expand All @@ -43,7 +43,7 @@ Finally, the `gasPayer` address can call the `MerkleAirdrop::claim` function on

```js
vm.prank(gasPayer);
airdrop.claim(user, amountToCollect, proof, v, r, s);
airdrop.claim(user, AMOUNT_TO_CLAIM, PROOF, v, r, s);
```

Afterward, we can verify that the test passes: the user's balance increases as expected, indicating that the `gasPayer` successfully claimed the tokens on the `user`'s behalf.
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ _Follow along with the video_
We can also run our `MerkleAirdrop.t::testUsersCanClaim` test on the ZKsync chain.

To do this, we can start by switching to the ZKsync version by running `foundryup --zksync`. Since the ZKsync compiler operates differently from the standard solc compiler, it's better to verify that everything builds correctly before deploying.
To do this, we can start by switching to the ZKsync version by running `foundryup-zksync`. Since the ZKsync compiler operates differently from the standard solc compiler, it's better to verify that everything builds correctly before deploying.

```js
forge build --ZK Sync
forge build --zksync
```

> 🗒️ **NOTE**:br
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function run() external {

The `claimAirdrop` function will take the deployed `MerkleAirdrop` address as a parameter.

First, we need to broadcast the transaction on the blockchain by wrapping our code between `vm.startBroadcast` and `vm.endBroadcast`.
First, we need to broadcast the transaction on the blockchain by wrapping our code between `vm.startBroadcast` and `vm.stopBroadcast`.

Next, we invoke the `MerkleAirdrop::claim` function, passing the following required parameters:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Copy the [Makefile content](https://github.com/Cyfrin/foundry-merkle-airdrop-cu/
To obtain the data for signing, use the `getMessageHash` function on the `MerkleAirdrop` contract. This function requires an account address, a `uint256` amount, and the Anvil node URL (`http://localhost:8545`).

```bash
cast call 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 "getMessageHash(address,uint256)" 0xf39Fd6e51aad88F6f4ce6aB88272ffFb92266 25000000000000000000 --rpc-url http://localhost:8545
cast call 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 "getMessageHash(address,uint256)" 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 25000000000000000000 --rpc-url http://localhost:8545
0x184e30c4b19f5e304a893524210d50346dad61c461e79155b910e73fd856dc72
```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
## Moccasin NFTs

This section is about Moccasin NFTs, and will continue to help us build our skills as we begin to work more independently with AI tools.

We'll be working with NFTs, building a decentralized stablecoin, understanding signatures and upgrades, and learning some amazing new things as we explore the industry and develop our own skills.

Remember to do all of the workshops associated with each of these sections. They are the heart of the course and how we will truly drill in all of the skills we're learning here.

Our first section is on Moccasin NFTs.

Here is the link to the full code we will be working with.

```
https://github.com/Cyfrin/mox-nft-cu
```

It's a classic Moccasin project and has a moccasin.toml file, which we will be diving into more in later sections.

The code we're working with here is pretty simple; just a basic NFT.
```python
from moccasin.boa_tools import VyperContract
from src import basic_nft

def deploy_basic_nft() -> VyperContract:
basic_nft_contract = basic_nft.deploy()
print(f"Deployed basic NFT to {basic_nft_contract.address}")
PUG_URI = "Qm1i6J98JYHB9Y36rUu0tDDm6LdEeNdAAgmrrx3s1tMa"
print(f"Minted Pug NFT with URI {PUG_URI}")
basic_nft_contract.mint(PUG_URI)
return basic_nft_contract

def moccasin_main():
return deploy_basic_nft()
```

This will deploy our basic NFT and give us a link to our IPFS file that will point to a JSON file that represents our NFT.

We can then import this into our MetaMask wallet and actually see our NFT.

We can then import this into our MetaMask wallet and actually see our NFT.

We'll also be going over some low-level encoding and some low-level raw calls.

Now, we will go over a slightly more advanced NFT. It's the Mood NFT. Let's go over it in the terminal.

```bash
mox run deploy_mood_nft --network anvil --account Vyper-cour
```

And this will get us a base 64 encoded URL where we can take this entire URL and then stick it into our browser to see our NFT which looks like this.

Now, we're going to go over the advanced, Sub-Lesson, where we learn how to call anything, we learn about encoding, we learn about raw calls, and a lot of the low-level functionality that we have been skipping over.

The final thing we will be showing you in this section, is this more advanced Sub-Lesson, where we learn how to call anything and we learn about encoding.

```python
from moccasin.boa_tools import VyperContract
from src import mood_nft

def deploy_mood_nft() -> VyperContract:
mood_nft_contract = mood_nft.deploy()
print(f"Deployed mood NFT to {mood_nft_contract.address}")
PUG_URI = "Qm1i6J98JYHB9Y36rUu0tDDm6LdEeNdAAgmrrx3s1tMa"
print(f"Minted Pug NFT with URI {PUG_URI}")
mood_nft_contract.mint(PUG_URI)
return mood_nft_contract

def moccasin_main():
return deploy_mood_nft()
```

Now, we will go over this more advanced sub-lesson where we learn how to call anything, we learn about encoding.

```python
from moccasin.boa_tools import VyperContract
from src import mood_nft

def deploy_mood_nft() -> VyperContract:
mood_nft_contract = mood_nft.deploy()
print(f"Deployed mood NFT to {mood_nft_contract.address}")
PUG_URI = "Qm1i6J98JYHB9Y36rUu0tDDm6LdEeNdAAgmrrx3s1tMa"
print(f"Minted Pug NFT with URI {PUG_URI}")
mood_nft_contract.mint(PUG_URI)
return mood_nft_contract

def moccasin_main():
return deploy_mood_nft()
```

We'll also be going over some low-level encoding and some low-level raw calls.

Stay tuned for more amazing things as we move forward in this course!
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## What happens when we stop our Anvil and Era Test Node?

When we stop our Era test node or Anvil node, our NFTs might still show up in our network, and they may go away.

If we start getting some weird errors, we can click on the account in the top right corner of MetaMask, click on Settings, Advanced, and then click on Clear activity tab data. This will do a kind of reset.

Obviously, right now, if we tried to send money because our Era test node is down, it would just gray out, which is really annoying. So, just know that we could even do a little refresh list, and it's going to just nothing's going to happen. So, just know that if our networks are down, if we're locally running networks are down, we might get some weird stuff showing up in our MetaMask. So, just be sure to keep that in mind.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
### Workshop 1: Mocksen NFTs

We are going to be working with a centralized gateway. The centralized option we are using is:

```text
https://gateway.pinata.cloud/ipfs/QmAnOFS6q43HRTW32wVSv6ITTFv3aKqFTf8qKFfTmciJMK
```

The first workshop involves completing two prompts:

1. **Upload your own dog image to IPFS, mint it as an NFT, and then see it in your Metamask!**
2. **Write tests to get at least 80% coverage!**

If we are using our own IPFS, our IPFS node must be running. Our desktop app has a node option in the top right. If we want to stop it, we can turn it off.

Alternatively, we can use a centralized service like Pinata Cloud. Pinata Cloud allows us to upload directly to the service.

We need to ensure that our token URI returns a JSON object. The JSON object must include the image and image attributes.

If we want to use the dog images provided in the Github repo for this course, we can find them under the /images/static directory.

### Workshop 2: Mocksen DeFi | Algorithmic Trading

The second workshop prompt is:

**Spend at most 25 minutes on all of these prompts without the aide of AI. If you�re unable to solve them after 25 minutes, stop, take a break, and then work with an AI or the discussions to help you solve them. Good luck!**

We can test our code in the terminal:

```bash
mox test --coverage
```

This completes our introduction to the Mocksen NFTs and testing workshops. Pause the video, complete these workshops, and we'll see you in a bit!
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## NFTs: What is an SVG?

We're going to use something called an SVG.

Now, an SVG is something that we can store all on chain, but the issue is that we have to be able to encode our image as an SVG. We can also encode our JSON on chain as a base 64 encoded URI.

What the heck do both of those mean? Well, first, let's go start with what is an SVG.

SVG stands for Scalable Vector Graphics. SVG defines vector-based graphics in XML format.

We can actually scroll down, and we can see an example right in this SVG example, right? So, this is it. So, it looks it's just kind of this tag where it has very specific parameters for defining what an image looks like.

And, the reason that SVGs are so cool is because no matter how big or small you make them, they're always going to have the exact same quality because they're scalable. You know how, like, if you take an image like this one, let me view this, you know, if we take this image, right, and I make it super super big, and I make it super big. The bigger I make it, the worse the quality gets. With an SVG, you don't ever have to deal with that because you define exactly what it will look like, no matter what size.

And what we can do is we can actually make our own SVG sort of like this, right? And if you're in the W3Schools, if you try for yourself, you can see my first SVG over here, and I can change this. I can say the fill is now blue. We'll run, and now it turns blue. I can say the stroke is black, right, and it turns black.

So, there's a ton of different parameters and functions that we can do to make an SVG look a certain way. Right, so if we're back in our VS Code, we can even go up to IMG, new file, example.svg. We can code some SVG in here, so we'll do SVG xml ns equals, and this is just version stuff, http.
```bash
cd img/
base64 -i example.svg
```
We can actually base 64 encode the output, everything in here, and what I can say is, I could do base 64 - i, which means we're going to input a file and we're going to pass in this example.svg. We'll see we get this weird thing as an output.

So, now if we take this weird thing, and I'm going to actually create a little little README to make some notes here.

So, this weird output is the base 64 encoded example.svg we just created.

Now, at the start of this, we can add a beginning piece to tell our browser that this is an SVG. So, I'm going to say data colon image slash svg plus xml colon base 64 comma like this. And, if I copy this whole thing, oops, sorry, this is in this should be a semicolon here. If I copy this whole thing, and I paste it into any browser, we're going to get this Hi, your browser decoded this. So, basically what we did was we encoded this SVG file, and put this data image, SVG plus xml colon base 64, so our browser knew how to decode it, and then just passed the entire image through our browser URL. And boom. So, we can also do this with images. So, if I go back to the repo associated with this lesson, go to images, dynamic NFT, we go to happy.svg, we go down to code instead of preview, and see the exact code here, right? So we create this viewBox, oops, we create some circles, we create this path, which is how we just kind of draw lines, and I can copy this whole thing, paste it into my image, so I'm going to say, oops, image, I'm going to do happy.svg, happy.svg, paste it in here. If I pull up the preview, I see the preview is a happy. Now what I can do is I can do base 64 - i happy.svg, we get this output. I can copy this output. Let me go over to the README, paste it. I'm going to add this beginning piece to it, and then copy this whole thing. We go back to my browser, paste it in, and boom. We've passed all of this data to generate this SVG right in the URL. And, this is looks like Yes it does. a token URI, right? So, now, instead of using an IPFS hash for our token URI, we can actually 100% on-chain use this SVG thing. And, because this SVG is basically coded on chain, we can update it and interact with it to make it do whatever we want it, right?

For example, if our has happy SVG, we could say, okay, if if somebody has 10 tokens, right, they get 10 circles or something like that, right? We could do whatever we want with this.
```bash
base64 -i happy.svg
```
Welcome back.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Now that we know what an SVG is and how we can code it, we can use this coding to build an NFT where all the metadata is stored directly on chain. Let's go ahead and build this NFT.

We'll call our new contract "mood_nft.vy" and inside we will code:

```javascript
pragma version 0.4.0
@license MIT
@title Mood NFT
```

We'll use a few imports here:

```javascript
from snekmate.tokens import erc721
from snekmate.auth import ownable as ow
```

We will then define a constructor for our contract:

```javascript
initializes:
ow
erc721: ownable = ow

def init():
ow._init()
erc721.init_(NAME, SYMBOL, BASE_URI, NAME, EIP_712_VERSION)
```

Finally, we'll need to define some state variables:

```javascript
# STATE VARIABLES
NAME: constant(String[25]) = "Mood NFT"
SYMBOL: constant(String[5]) = "MNFT"
BASE_URI: public(constant(String[34])) = "https://gateway.pinata.cloud/ipfs/"
EIP_712_VERSION: constant(String[1]) = "1"
```

We are now ready to start deploying our dynamic NFT, which we'll do in the next video!
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
## Base64 Encoding Images

In this lesson, we will learn how to encode an image to Base64.

First, we will create a new folder called "images" and save our two SVG files (happy.svg and sad.svg) in it.

Next, we will import the base64 library and create a new function:

```python
import base64

def svg_to_base64_uri(svg):
svg_bytes = svg.encode("utf-8")
base64_bytes = base64.b64encode(svg_bytes).decode("utf-8")
return f"data:image/svg+xml;base64,{base64_bytes}"
```

Now, we can use the `svg_to_base64_uri()` function to encode our SVGs to Base64 and assign the results to the `happy_svg_uri` and `sad_svg_uri` variables.

```python
def deploy_mood():
happy_svg_uri = ""
sad_svg_uri = ""
with open("./images/happy.svg", "r") as f:
happy_svg = f.read()
happy_svg_uri = svg_to_base64_uri(happy_svg)
print(happy_svg_uri)

with open("./images/sad.svg", "r") as f:
sad_svg = f.read()
sad_svg_uri = svg_to_base64_uri(sad_svg)

mood_nft = mood_nft.deploy(happy_svg_uri, sad_svg_uri)
```

Finally, we will import the "mood_nft" contract and use it to pass our encoded image data.

```python
from src import mood_nft
```

```python
def deploy_mood():
happy_svg_uri = ""
sad_svg_uri = ""
with open("./images/happy.svg", "r") as f:
happy_svg = f.read()
happy_svg_uri = svg_to_base64_uri(happy_svg)
print(happy_svg_uri)

with open("./images/sad.svg", "r") as f:
sad_svg = f.read()
sad_svg_uri = svg_to_base64_uri(sad_svg)

mood_nft = mood_nft.deploy(happy_svg_uri, sad_svg_uri)
```

We can then run our script using the following terminal command:

```bash
mox run deploy_mood_nft
```

This will return a Base64 encoded string that can be used to render our SVG image in a browser. We can copy and paste the string into our browser to verify that it works as expected.

We will then make the `happy_svg_uri` and `sad_svg_uri` constant variables in our contract file.

```python
HAPPY_SVG_URI: immutable(String[800]) = ""
SAD_SVG_URI: immutable(String[800]) = ""
```

We will pass these into the constructor of the contract so the image data is stored there.

```python
def init(happy_svg_uri: String[800], sad_svg_uri: String[800]):
ow.init()
erc721.init(NAME, SYMBOL, BASE_URI, NAME, EIP_712_VERSION)
HAPPY_SVG_URI = happy_svg_uri
SAD_SVG_URI = sad_svg_uri
```
Loading

0 comments on commit 3d2694f

Please sign in to comment.