<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jarrod Watts</title>
    <description>The latest articles on DEV Community by Jarrod Watts (@jarrodwattsdev).</description>
    <link>https://dev.to/jarrodwattsdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F894739%2F67ae58ec-c1ba-4c3c-a23c-382564a13622.jpg</url>
      <title>DEV Community: Jarrod Watts</title>
      <link>https://dev.to/jarrodwattsdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jarrodwattsdev"/>
    <language>en</language>
    <item>
      <title>zkEVMs and the Race to Scale Ethereum</title>
      <dc:creator>Jarrod Watts</dc:creator>
      <pubDate>Tue, 21 Feb 2023 01:04:58 +0000</pubDate>
      <link>https://dev.to/jarrodwattsdev/zkevms-and-the-race-to-scale-ethereum-4o3h</link>
      <guid>https://dev.to/jarrodwattsdev/zkevms-and-the-race-to-scale-ethereum-4o3h</guid>
      <description>&lt;p&gt;In the past week, the race to launch an EVM-compatible ZK rollup has been heating up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/0xPolygon/status/1625529122561597440" rel="noopener noreferrer"&gt;Polygon announced the launch date for their zkEVM mainnet&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://twitter.com/zksync/status/1626235292268240902" rel="noopener noreferrer"&gt;zkSync launched their zkEVM mainnet&lt;/a&gt; &lt;em&gt;(just two days later!)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why's this happening? WTF is a zkEVM? How do I use one?&lt;/p&gt;

&lt;p&gt;Let's dive into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, what's wrong with Ethereum?
&lt;/h2&gt;

&lt;p&gt;Ethereum Mainnet is able to process roughly &lt;strong&gt;15 transactions&lt;/strong&gt; per second. We all know what this means; huge gas prices and long finality times, leading to a grim user experience.&lt;/p&gt;

&lt;p&gt;Depending on how deep down the rabbit hole you are at this point, you may already be familiar with solutions to address this; sidechains, layer 2s, rollups... &lt;em&gt;all that good stuff&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;These were all built because Ethereum is &lt;em&gt;currently&lt;/em&gt; too popular for what it is capable of handling. Despite this, the Ethereum Foundation has agreed they are not going to sacrifice security/decentralization to improve scalability.&lt;/p&gt;

&lt;p&gt;This balance between decentralization, security, and scalability is known as the &lt;a href="https://ethereum.org/en/upgrades/vision/" rel="noopener noreferrer"&gt;scalability trilemma&lt;/a&gt;. The long-term vision of Ethereum is to achieve all three of these qualities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8hcqckh6k263obmccv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8hcqckh6k263obmccv5.png" alt="scalability trilemma of blockchains" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should we improve scalability?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ethereum.org/en/developers/docs/scaling/" rel="noopener noreferrer"&gt;Scalability&lt;/a&gt; refers to how well Ethereum can handle the demand for transactions to be processed. If more transactions can be processed faster, two things happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://ethereum.org/en/developers/docs/gas/#top" rel="noopener noreferrer"&gt;Gas prices&lt;/a&gt; go down&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/#finality" rel="noopener noreferrer"&gt;Finality&lt;/a&gt; &lt;em&gt;(how fast your transactions are finalized)&lt;/em&gt; gets faster&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, if we're not going to sacrifice decentralization, how do we make Ethereum more scalable? There are multiple different solutions that are either already being used today, in the research/testing phase, or recently shipped to mainnet 😉.&lt;/p&gt;

&lt;p&gt;Let's explore some of the existing solutions first, why they have problems of their own, and then dive into ZK EVMs as the potential "final boss" of scaling solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's been built to address this so far?
&lt;/h2&gt;

&lt;p&gt;Most solutions built to address the scalability of Ethereum usually come with some kind of sacrifice in the other two qualities in the scalability trilemma.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"it scales better... buuuuuut &amp;lt;insert downside here&amp;gt;",&lt;/em&gt; is usually how it goes*.*&lt;/p&gt;

&lt;p&gt;Let's take a closer look at each of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sidechains
&lt;/h3&gt;

&lt;p&gt;A sidechain is a completely separate blockchain from Ethereum, with different histories, roadmaps, and even &lt;a href="https://ethereum.org/en/developers/docs/scaling/sidechains/#consensus-algorithms" rel="noopener noreferrer"&gt;consensus algorithms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order for a side-chain to actually &lt;em&gt;be&lt;/em&gt; a side-chain, it is required to have a two-way &lt;a href="https://ethereum.org/en/bridges/" rel="noopener noreferrer"&gt;bridge&lt;/a&gt; connecting it with Ethereum Mainnet, where data can be transferred between the two chains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://polygon.technology/solutions/polygon-pos" rel="noopener noreferrer"&gt;Polygon's PoS&lt;/a&gt; (&lt;a href="https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/" rel="noopener noreferrer"&gt;proof-of-stake&lt;/a&gt;) is the most popular example of a sidechain.&lt;/p&gt;

&lt;p&gt;Polygon PoS boasts a ~10,000x lower gas fee per transaction, and ~450x increase in transactions per second than Ethereum. Polygon PoS is awesome. Some of the biggest brands in the world have chosen Polygon as their partner; Adidas, Meta, Stripe, Reddit, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FFpLekSCXwAMbZec%3Fformat%3Djpg%26name%3Dmedium" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FFpLekSCXwAMbZec%3Fformat%3Djpg%26name%3Dmedium" alt="partners of Polygon" width="1123" height="1124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, sidechains sacrifice &lt;em&gt;some&lt;/em&gt; measure of decentralization or security to achieve this higher throughput.&lt;/p&gt;

&lt;p&gt;You can dive deeper into the risk assessment of scaling solutions on sites such as &lt;a href="https://l2beat.com/scaling/risk" rel="noopener noreferrer"&gt;L2BEAT&lt;/a&gt;. For example, if we take a look at Polygon PoS, we can see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta47i31blf2wfbe9filb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta47i31blf2wfbe9filb.png" alt="L2BEAT risk assessment of Polygon PoS" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, despite these sacrifices, Polygon PoS has had struggles with scalability several times in the past.&lt;/p&gt;

&lt;p&gt;For example, in 2022, a play-to-earn game called Sunflower Farmers sent gas prices skyrocketing after the game took up over 40% of the network's gas fees.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1478613172487602179-536" src="https://platform.twitter.com/embed/Tweet.html?id=1478613172487602179"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1478613172487602179-536');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1478613172487602179&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;More recently, transactions were failing from users' wallets due to incorrect gas estimations, as a result of the high demand on Polygon PoS.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1626195644573425665-847" src="https://platform.twitter.com/embed/Tweet.html?id=1626195644573425665"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1626195644573425665-847');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1626195644573425665&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Rollups
&lt;/h2&gt;

&lt;p&gt;Rollups are different from sidechains in the sense that they're not "separate" from Ethereum. Instead, they publish transaction results onto Ethereum mainnet; allowing them to derive the full benefits of Ethereum's security in the process.&lt;/p&gt;

&lt;p&gt;Rollups work by allowing operators to bundle multiple off-chain transactions together outside the main &lt;a href="https://ethereum.org/en/developers/docs/evm/" rel="noopener noreferrer"&gt;EVM&lt;/a&gt;, and then submit them all together to Ethereum; achieving 10 to 100 times improvements in terms of scalability.&lt;/p&gt;

&lt;p&gt;On a technical level, these batches are submitted to a "rollup" smart contract that is stored on Ethereum mainnet; which keeps track of the rollup's state.&lt;/p&gt;

&lt;p&gt;There are two kinds of rollups that exist today:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Optimistic rollups&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zero Knowledge (ZK) Rollups&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A &lt;a href="https://ethereum.org/en/developers/docs/bridges/" rel="noopener noreferrer"&gt;bridge&lt;/a&gt; allows you to transfer funds or data between Ethereum mainnet and a rollup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimistic Rollups
&lt;/h3&gt;

&lt;p&gt;Optimistic rollups such as &lt;a href="https://www.optimism.io/" rel="noopener noreferrer"&gt;Optimism&lt;/a&gt; and &lt;a href="https://arbitrum.io/" rel="noopener noreferrer"&gt;Arbitrum One&lt;/a&gt; are called "optimistic" because they &lt;strong&gt;assume&lt;/strong&gt; the transactions that they roll up are valid &lt;strong&gt;by default&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They don't post any proof that the transactions are valid to Ethereum. Instead, transactions are "innocent until proven guilty", which includes a fraud-proving scheme that enables the detection of invalid transactions.&lt;/p&gt;

&lt;p&gt;A "challenge period" is available for parties to contest the results of a rollup transaction by submitting a &lt;a href="https://ethereum.org/en/glossary/#fraud-proof" rel="noopener noreferrer"&gt;&lt;strong&gt;fraud-proof&lt;/strong&gt;&lt;/a&gt; that can see whether or not a fraudulent transaction took place in that rollup.&lt;/p&gt;

&lt;p&gt;If one is detected, the transactions are re-executed and the state of the rollup is updated. The party who submits the fraudulent transactions incur a penalty, creating an incentive mechanism within the ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Okay, this sounds great. What's the downside?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because of this challenge period, (usually around 7 days), you &lt;strong&gt;must wait&lt;/strong&gt; until the challenge period is over before you can perform this withdrawal to Ethereum mainnet over the bridge.&lt;/p&gt;

&lt;p&gt;There's also &lt;em&gt;some&lt;/em&gt; level of reliance that at least &lt;em&gt;one&lt;/em&gt; honest person is checking for fraudulent transactions to challenge the state of the rollup.&lt;/p&gt;

&lt;h3&gt;
  
  
  ZK Rollups
&lt;/h3&gt;

&lt;p&gt;Alright, we're almost at the juicy part.&lt;/p&gt;

&lt;p&gt;ZK rollups, unlike optimistic rollups, &lt;strong&gt;do&lt;/strong&gt; post &lt;a href="https://ethereum.org/en/glossary/#validity-proof" rel="noopener noreferrer"&gt;validity proofs&lt;/a&gt; to prove the correctness of the changes they post to Ethereum mainnet.&lt;/p&gt;

&lt;p&gt;These validity proofs are "zero-knowledge proofs"; meaning the details of the statement that the rollup provides can be proven as true, without revealing the actual contents of that statement, using either &lt;a href="https://arxiv.org/abs/2202.06877" rel="noopener noreferrer"&gt;zk-SNARKs&lt;/a&gt; or &lt;a href="https://eprint.iacr.org/2018/046" rel="noopener noreferrer"&gt;zk-STARKs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ethereum.org/en/developers/docs/scaling/zk-rollups/#validity-proofs-in-zk-rollups" rel="noopener noreferrer"&gt;Learn more about how validity proofs work in zk-rollups&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This removes the requirement for a challenge period, as the transactions are provably true as soon as the rollup contract verifies the validity proof. This means you can withdraw funds from ZK rollups to Ethereum mainnet without waiting!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Awesome, but what's the catch?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unlike the other solutions we've talked about so far, ZK Rollups are &lt;strong&gt;not&lt;/strong&gt; compatible with the EVM. This means you can't just point your Solidity contract at a ZK Rollup and ship it, which you &lt;em&gt;can&lt;/em&gt; do with all of the other solutions we've described.&lt;/p&gt;

&lt;p&gt;Whereas, with other solutions, you can simply change the URL of your deployment script and deploy the exact same code to Ethereum, Polygon PoS, Optimism, Arbitrum, etc.&lt;/p&gt;

&lt;p&gt;That's the catch. You &lt;strong&gt;cannot&lt;/strong&gt; do this with ZK Rollups... Until now!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter, zkEVMs&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a zkEVM?
&lt;/h2&gt;

&lt;p&gt;The main reason ZK rollups are not utilized is that they have not been EVM-compatible in the past, making it extremely difficult to actually build anything on them.&lt;/p&gt;

&lt;p&gt;If I've done a good job explaining things so far, the name "zkEVM" is hopefully self-explanatory to you... &lt;strong&gt;A zkEVM is a zero-knowledge rollup, that &lt;em&gt;is&lt;/em&gt; EVM compatible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;zkEVMs enable all the benefits of a ZK Rollup, with the same developer experience of building on any other EVM chain like Polygon or Ethereum. Combining the best of both worlds, and potentially providing an answer to the scalability trilemma.&lt;/p&gt;

&lt;p&gt;This technology unlocks all existing web3 dapps to simply change their deployment scripts to a different RPC URL and ship directly to the zkEVM, exactly the same way they do now.&lt;/p&gt;

&lt;p&gt;zkEVMs handle the batching of transaction execution and post cryptographic proof that the result of those transactions is correct to Ethereum mainnet, all while maintaining EVM compatibility.&lt;/p&gt;

&lt;p&gt;This means that every smart contract on Ethereum or Polygon PoS can now also be deployed to a zkEVM. Drastically improving the scalability of the smart contracts and the dapps that use them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"In the medium to long term, ZK rollups will win out in all use cases as ZK-SNARK technology improves."&lt;/em&gt; - &lt;a href="https://vitalik.ca/general/2021/01/05/rollup.html" rel="noopener noreferrer"&gt;Vitalik Buterin&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I build on a ZK EVM?
&lt;/h3&gt;

&lt;p&gt;With all of the hype and releases of ZK EVMs, how do you actually build on one?&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://thirdweb.com/" rel="noopener noreferrer"&gt;thirdweb&lt;/a&gt; &lt;em&gt;(where I currently work)&lt;/em&gt;, we just shipped support for &lt;em&gt;any&lt;/em&gt; EVM chain, including all the zkEVMs such as Polygon zkEVM, zkSync, Scroll and more.&lt;/p&gt;

&lt;p&gt;Consider checking out the below guide I helped review by my awesome colleague and friend @&lt;a href="https://dev.to@avneesh0612"&gt;Avneesh Agarwal&lt;/a&gt;, which shows you how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add a ZK EVM network to your wallet&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bridge funds from Ethereum to the ZK Rollup&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy your Solidity smart contract to a ZK EVM&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://blog.thirdweb.com/guides/how-to-deploy-a-smart-contract-to-polyon-zkevm-testnet/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thirdweb.com%2Fcontent%2Fimages%2Fsize%2Fw1200%2F2023%2F03%2Fpolygon-zkevm-thumbnail-1-2.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://blog.thirdweb.com/guides/how-to-deploy-a-smart-contract-to-polyon-zkevm-testnet/" rel="noopener noreferrer" class="c-link"&gt;
          How to Deploy a smart contract to Polygon zkEVM Testnet
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn how to create and deploy a smart contract to the Polygon zkEVM testnet. Write a smart contract in solidity and deploy using thirdweb.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.thirdweb.com%2Fcontent%2Fimages%2Fsize%2Fw256h256%2F2022%2F08%2Ftw-logo-newsletter-31.png" width="256" height="256"&gt;
        blog.thirdweb.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Or check out this 3-minute video to shipping an NFT collection on Polygon's zkEVM:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1627520959854022656-129" src="https://platform.twitter.com/embed/Tweet.html?id=1627520959854022656"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1627520959854022656-129');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1627520959854022656&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;That's it! I've tried my best to condense the past few weeks of reading about Ethereum scaling solutions into a 5 minute read for you :)&lt;/p&gt;

&lt;p&gt;What do you think? Are zkEVMs the holy grail for scaling Ethereum? Or are we just getting started? Let me know!&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, there'll be many more just like it coming soon. Consider following me to get notified when those are released!&lt;/p&gt;

</description>
      <category>cursorai</category>
      <category>agile</category>
      <category>productivity</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>How Account Abstraction Will Change Web3 UX Forever</title>
      <dc:creator>Jarrod Watts</dc:creator>
      <pubDate>Tue, 24 Jan 2023 01:09:16 +0000</pubDate>
      <link>https://dev.to/jarrodwattsdev/how-account-abstraction-will-change-web3-ux-forever-4b00</link>
      <guid>https://dev.to/jarrodwattsdev/how-account-abstraction-will-change-web3-ux-forever-4b00</guid>
      <description>&lt;p&gt;In this post, we'll explore the issues with UX in the current web3 landscape, and dive deeper into how a proposal known as &lt;strong&gt;account abstraction&lt;/strong&gt; provides a potential solution to several of the biggest problems we have today.&lt;/p&gt;

&lt;p&gt;The reality is, a poor UX is a significant drawback to building your application in a decentralized manner. While there are many solutions actively being worked on to address the issues, account abstraction is arguably the most promising.&lt;/p&gt;

&lt;p&gt;It's an exciting paradigm that will allow anybody to interact with &lt;a href="https://ethereum.org/en/developers/docs/dapps/" rel="noopener noreferrer"&gt;dapps&lt;/a&gt;; not just web3 enthusiasts, and is coming to reality with the latest Ethereum Improvement Proposal &lt;a href="https://eips.ethereum.org/EIPS/eip-4337" rel="noopener noreferrer"&gt;EIP-4337&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's dive into it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Ethereum Accounts &amp;amp; Transactions Recap
&lt;/h2&gt;

&lt;p&gt;Let's get through the boring stuff first. To understand account abstraction, we first need to understand what an account actually &lt;em&gt;is&lt;/em&gt; when talking about Ethereum.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Ethereum Accounts?
&lt;/h3&gt;

&lt;p&gt;Ethereum has two different types of "accounts":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Contract Accounts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Externally Owned Accounts (EOAs)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can think of a contract account as &lt;strong&gt;code&lt;/strong&gt; (smart contracts) living on the blockchain that defines how the account behaves, and think of EOAs as a &lt;strong&gt;person&lt;/strong&gt; (&lt;em&gt;although a person could have many EOAs)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You probably are already familiar with EOAs. Your MetaMask wallet is an EOA. EOAs are made up of a cryptographic pair of keys: public and private keys that control account activities.&lt;/p&gt;

&lt;p&gt;Contract accounts, however, don't have a private key. They're smart contracts that are controlled by the logic of the code within them; they're not controlled by a user.&lt;/p&gt;

&lt;p&gt;The key takeaway here is that &lt;em&gt;code&lt;/em&gt; defines what contract accounts do, and &lt;em&gt;users&lt;/em&gt; control what EOAs do. This matters because smart contracts have the capability to do anything you can write in code, whereas EOAs can basically just sign transactions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9lil04d3t7nvd50ecqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq9lil04d3t7nvd50ecqj.png" alt="contract accounts vs eoas" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Ethereum Transactions?
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Alright, so that's&lt;/em&gt; &lt;a href="https://ethereum.org/en/developers/docs/accounts/" rel="noopener noreferrer"&gt;&lt;em&gt;accounts&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. What about&lt;/em&gt; &lt;a href="https://ethereum.org/en/developers/docs/transactions/" rel="noopener noreferrer"&gt;&lt;em&gt;transactions&lt;/em&gt;&lt;/a&gt;&lt;em&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every time you want to write information to the blockchain, such as transfer tokens, or mint an NFT, a &lt;strong&gt;transaction&lt;/strong&gt; needs to occur. &lt;strong&gt;Transactions&lt;/strong&gt; need to be signed by an EOA, and an EOA also has to pay the associated gas fees.&lt;/p&gt;

&lt;p&gt;A transaction is initiated &lt;em&gt;by&lt;/em&gt; an EOA, and can be sent &lt;em&gt;to&lt;/em&gt; either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Another EOA, for example, an EOA transferring ETH to another EOA.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A contract account, for example, minting an NFT from a drop.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How Web3 Works Today: EOAs &amp;amp; A Poor UX
&lt;/h3&gt;

&lt;p&gt;Performing actions on the blockchain today is typically slow and tedious. Every time you want to write new information to the blockchain, you sign a transaction from your EOA to do so.&lt;/p&gt;

&lt;p&gt;Once you're familiar with the process, this becomes the standard experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For new users, however, it's a &lt;em&gt;nightmare&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The process of starting from scratch and interacting with a web3 application for the very first time is enough to put anyone off from entering the space, and that's &lt;em&gt;just&lt;/em&gt; the beginning.&lt;/p&gt;

&lt;p&gt;Here's a step-by-step experience that a new user goes through to perform their first action on a decentralized application from a fresh EOA:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj62lqxf09smb3k6q4w4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj62lqxf09smb3k6q4w4.png" alt="eoa onboarding flow for new users" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This experience is brutal for any new user, tech-savvy or not. But it doesn't stop there, the initial setup is just the beginning of the user's concerns when using EOAs.&lt;/p&gt;

&lt;h3&gt;
  
  
  EOAs Are Extremely Risky
&lt;/h3&gt;

&lt;p&gt;You're likely already familiar with somebody you know losing access to their EOA by either accidentally sharing or losing access to their private key. Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.independent.co.uk/tech/bitcoin-exchange-quadrigacx-password-cryptocurrency-scam-a8763676.html" rel="noopener noreferrer"&gt;Millions of dollars of cryptocurrency 'lost' after man &lt;strong&gt;dies&lt;/strong&gt; with only password&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.cbc.ca/radio/asithappens/as-it-happens-friday-edition-1.5875363/this-man-owns-321m-in-bitcoin-but-he-can-t-access-it-because-he-lost-his-password-1.5875366#:~:text=Back%20in%202011%2C%20he%20produced,wallet%20that%20holds%20his%20bitcoins." rel="noopener noreferrer"&gt;Man owns $321M in bitcoin but he can't access it because he &lt;strong&gt;lost&lt;/strong&gt; his password&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://finbold.com/guy-locks-phone-with-potentially-6-million-in-crypto-heres-how-much-the-hacker-recovered/" rel="noopener noreferrer"&gt;Guy locks phone with potentially $6 million in crypto&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The level of responsibility you have with traditional EOAs is &lt;strong&gt;dangerously&lt;/strong&gt; high.&lt;/p&gt;

&lt;p&gt;There's even a saying for it: &lt;strong&gt;"not your keys, not your crypto"&lt;/strong&gt;; referring to the fact that if somebody else ever has your private key at &lt;em&gt;any&lt;/em&gt; point (such as a centralized exchange), they have the power to control your funds; this point has been proven countless times in the past.&lt;/p&gt;

&lt;p&gt;The harsh reality is that &lt;strong&gt;private keys are easy to lose and impossible to recover&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkf3zlyiichv8okmqvbc2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkf3zlyiichv8okmqvbc2.gif" alt="with great power comes great responsibility gif" width="500" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  EOAs Have Limited Capabilities
&lt;/h3&gt;

&lt;p&gt;As we touched on earlier, EOAs are very limited in their capabilities.&lt;/p&gt;

&lt;p&gt;From your EOA, you're usually performing one of two typical actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Submitting a transaction to transfer tokens to another EOA&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Submitting a transaction that executes a function on a contract account&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Whoever has the private key can sign messages and initiate &lt;em&gt;any&lt;/em&gt; transactions the EOA can handle. Knowing the private key of an EOA gives you the power to perform &lt;strong&gt;everything&lt;/strong&gt; that an EOA is capable of. It's all or nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  EOAs Will Never Enable Mainstream Adoption
&lt;/h3&gt;

&lt;p&gt;In the real world, losing your credit card doesn't mean you are completely doomed.&lt;/p&gt;

&lt;p&gt;There are rules in place that allow you to do things like set payment limits, stop transactions, detect fraud, change funds to a new account, only allow funds to be transferred under certain conditions, etc.&lt;/p&gt;

&lt;p&gt;In web3, if you make &lt;em&gt;one&lt;/em&gt; mistake, your &lt;strong&gt;entire&lt;/strong&gt; account is compromised and unrecoverable. EOAs even compared to centralized stores of currency is... 💩.&lt;/p&gt;

&lt;p&gt;We've dunked on EOAs enough, let's finally discuss the solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Account Abstraction?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Account abstraction is the proposal to allow users to use smart contract wallets instead of EOAs.&lt;/strong&gt; This completely removes any need for users to use EOAs in order to perform transactions.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But why? What do contract accounts do that EOAs can't?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Smart contracts are infinitely more flexible in their capabilities than EOAs. Each smart contract can define different rules and configurations within its code.&lt;/p&gt;

&lt;p&gt;Some examples of use cases can be seen below:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;EOAs&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Contract Accounts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Permission controls&lt;/td&gt;
&lt;td&gt;Private key grants full access to everything.&lt;/td&gt;
&lt;td&gt;Define a list of tiered permission levels. e.g. Require 3 out of 5 signers to approve a transaction.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch transactions&lt;/td&gt;
&lt;td&gt;Each individual action requires a separate signature.&lt;/td&gt;
&lt;td&gt;Capable of batching transactions together; e.g. approving a token transfer and transferring a token in the same operation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account Recovery&lt;/td&gt;
&lt;td&gt;Loss or exposure of private key means full loss of control over the wallet.&lt;/td&gt;
&lt;td&gt;There is no private key. You can write any arbitrary logic in code that allows you to recover the funds in the wallet.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction limits&lt;/td&gt;
&lt;td&gt;Any transaction your wallet signs is what occurs. You cannot restrict anything.&lt;/td&gt;
&lt;td&gt;Write any logic to control how funds can be transferred. E.g. a function to halt transactions to other addresses while you recover your account.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are just a few of the capabilities that contract accounts offer over traditional EOAs. The key thing is; &lt;strong&gt;contract accounts are code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This means a*nything* you can write in code is therefore possible in a contract account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0hvs67t5bpz45z4hx86.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0hvs67t5bpz45z4hx86.gif" alt="anything is possible gif" width="480" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  History of Account Abstraction Proposals
&lt;/h3&gt;

&lt;p&gt;Okay, this sounds great. Why aren't we already doing this today? Before we answer that, let's quickly give a brief overview of the history of account abstraction proposals dating back to 2016, and explore why EIP-4337 is different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2016:&lt;/strong&gt; &lt;a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-86.md" rel="noopener noreferrer"&gt;EIP-86&lt;/a&gt; - Proposal allowing users to create "account contracts" that perform any desired signature/nonce checks instead of using the mechanism that is currently hard-coded into transaction processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2020:&lt;/strong&gt; &lt;a href="https://eips.ethereum.org/EIPS/eip-2938" rel="noopener noreferrer"&gt;EIP-2938&lt;/a&gt; - Proposal to create a new transaction with type &lt;code&gt;AA_TX_TYPE&lt;/code&gt;. Transactions of this type are referred to as “AA transactions”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2020:&lt;/strong&gt; &lt;a href="https://eips.ethereum.org/EIPS/eip-3074" rel="noopener noreferrer"&gt;EIP-3074&lt;/a&gt; - Proposal allowing users to delegate control of their EOA to a smart contract. Would allow any EOA to act like a smart contract wallet without deploying a contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None of these proposals have been merged into Ethereum&lt;/strong&gt;. They are all currently in the "stagnant" category; meaning they have been inactive for a period of 6 months or greater.&lt;/p&gt;

&lt;p&gt;Part of the reason for these proposals not being merged is that they require consensus-layer protocol changes to the Ethereum network.&lt;/p&gt;

&lt;p&gt;Until 2021, when &lt;a href="https://eips.ethereum.org/EIPS/eip-4337" rel="noopener noreferrer"&gt;EIP-4337&lt;/a&gt; was proposed; account abstraction on Ethereum &lt;em&gt;without&lt;/em&gt; a consensus layer change required!&lt;/p&gt;

&lt;h3&gt;
  
  
  EIP-4337: Account Abstraction Using Alt Mempool
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://eips.ethereum.org/EIPS/eip-4337" rel="noopener noreferrer"&gt;EIP-4337&lt;/a&gt; introduces a *"*pseudo-transaction" object called a &lt;code&gt;UserOperation&lt;/code&gt;; a structure that describes a transaction to be sent on behalf of a user.&lt;/p&gt;

&lt;p&gt;User Operations go into an "alt mempool"; which is essentially a waiting room for storing information on unconfirmed transactions.&lt;/p&gt;

&lt;p&gt;Nodes on the Ethereum network can choose to act as a "bundler". Bundlers pick up user operations from the mempool, and package multiple user operations into a single transaction known as a "bundle transaction".&lt;/p&gt;

&lt;p&gt;Once they create a bundle transaction, they send it to a global "&lt;a href="https://eips.ethereum.org/EIPS/eip-2470#:~:text=Some%20contracts%20are%20designed%20to,%2D1820%20and%20EIP%2D2429." rel="noopener noreferrer"&gt;singleton&lt;/a&gt;" smart contract known as the "EntryPoint". There is only one EntryPoint smart contract on the entire blockchain. The bundler calls a function on the EntryPoint smart contract called &lt;code&gt;handleOps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This function receives the bundle transaction, and calls a special function on each account: &lt;code&gt;validateUserOp&lt;/code&gt;. Each smart contract wallet must implement this function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;validateUserOp&lt;/code&gt; should verify the operation’s signature, and pay the fee if the account considers the operation valid, before continuing to execute the operation.&lt;/p&gt;

&lt;p&gt;Each smart contract wallet also must implement a second function: expected to be called "&lt;code&gt;execute&lt;/code&gt;" to actually perform the operation that is sent in by the EntryPoint contract.&lt;/p&gt;

&lt;p&gt;A simplified flow of this can be seen below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n4bol00z7it4p0q5tlm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n4bol00z7it4p0q5tlm.png" alt="summary of account abstraction in eip-4337" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Does This Matter?
&lt;/h3&gt;

&lt;p&gt;Contract accounts are the next evolution of wallets required to provide a much-needed improvement to the UX of web3.&lt;/p&gt;

&lt;p&gt;The possibilities are really endless for what this change enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Creating wallets for your users under the hood when they sign up for your app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Session keys for web3 games (allow any X transaction for Y amount of time without the need for signatures on each transaction)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Team wallets to use decentralized applications with tiered permissions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A grandma could be collecting NFTs and not even know what the blockchain is. Account abstraction enables everybody to use web3; not just tech enthusiasts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;In this post, we've outlined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The fundamental concepts of accounts and transactions on Ethereum.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How EOAs fall short in terms of web3 user experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What account abstraction does to solve it and how it works under the hood.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Account abstraction is a game changer for web3 and bringing decentralized applications to a mainstream audience.&lt;/p&gt;

&lt;p&gt;The power to use smart contracts as your wallet brings endless possibilities and EIP-4337 is the latest proposal of account abstraction that doesn't require a consensus-layer change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thanks For Reading!
&lt;/h3&gt;

&lt;p&gt;I appreciate you making it this far! 🙏&lt;/p&gt;

&lt;p&gt;If you enjoyed this content, consider following me for more!&lt;/p&gt;

</description>
      <category>welcome</category>
    </item>
    <item>
      <title>How to ACTUALLY Become a Web3 Developer in 2023</title>
      <dc:creator>Jarrod Watts</dc:creator>
      <pubDate>Fri, 06 Jan 2023 06:13:43 +0000</pubDate>
      <link>https://dev.to/jarrodwattsdev/how-to-actually-become-a-web3-developer-in-2023-539p</link>
      <guid>https://dev.to/jarrodwattsdev/how-to-actually-become-a-web3-developer-in-2023-539p</guid>
      <description>&lt;p&gt;This is an &lt;strong&gt;opinionated&lt;/strong&gt; guide to becoming a web3 developer in 2023.&lt;/p&gt;

&lt;p&gt;I'll outline the exact steps that I personally took to land a role as a developer relations engineer at &lt;a href="https://thirdweb.com/" rel="noopener noreferrer"&gt;thirdweb&lt;/a&gt;; a suite of developer tools for building in web3.&lt;/p&gt;

&lt;p&gt;Unlike other roadmaps, I'm not going to give you 300 things to learn and get you stuck in tutorial hell. This is an actionable, sequence of technologies for you to learn, including the exact resources I found most helpful throughout my journey.&lt;/p&gt;

&lt;p&gt;Let's dive into it! Below is a preview of each layer of the stack we'll be covering.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnkotoh9hne1w13oeide.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnkotoh9hne1w13oeide.png" alt="web3 tech stack each layer" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Disclaimer (READ THIS)
&lt;/h2&gt;

&lt;p&gt;Consuming resources alone will never teach you how to become a developer.&lt;/p&gt;

&lt;p&gt;Every dev has to constantly fight their own battles on the journey to understanding how to code, and put together the pieces of the gigantic puzzle along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;As soon as you know how to START building, exit the roadmap and go at it alone. Just &lt;em&gt;try&lt;/em&gt; to build something. It's going to suck. And that's the point!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Come back to the resources to fill in the gaps in your knowledge as you get stuck; learn what tools are available for you to use that solve your problems with the resources in this guide.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every project you make will be better than the last, and you'll soon be embarrassed by your old code.&lt;/p&gt;

&lt;p&gt;DO NOT &lt;em&gt;just&lt;/em&gt; consume these resources, they are only there to help point you in the right direction in a logical order as you &lt;strong&gt;actually build your own projects.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvif8ruuejdt8rr6s1o4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvif8ruuejdt8rr6s1o4.gif" alt="Buckle up" width="1024" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's not going to be easy, but it is worth it. Buckle up and let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Roadmap
&lt;/h2&gt;

&lt;p&gt;We're going to cover a lot of topics in this roadmap, and I'll provide you with resources that I personally used to develop my understanding at each step of the way. Here's a quick preview of some of the tools we'll cover:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uuv4kmunopgcebwss61.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uuv4kmunopgcebwss61.png" alt="Roadmap overview" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fundamentals: HTML &amp;amp; CSS
&lt;/h2&gt;

&lt;p&gt;Every website consists of three things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt;: Structure of elements on the page&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSS&lt;/strong&gt;: Style of elements on the page&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript&lt;/strong&gt;: Interactivity of elements on the page&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9n77r1wnz1a4ahmhir4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9n77r1wnz1a4ahmhir4z.png" alt="html css and javascript" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any application you build, no matter what technology you use, is eventually transformed into these three languages and served to a user on a web page.&lt;/p&gt;

&lt;p&gt;Hence it's an important foundational piece of knowledge to understand the very basics of HTML and CSS first before diving into any "programming" (adding interactivity or logic to your application).&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML &amp;amp; CSS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://pll.harvard.edu/course/cs50-introduction-computer-science?delta=0" rel="noopener noreferrer"&gt;CS50&lt;/a&gt; is Harvard's introductory course to computer science. It's an outstanding resource taught by the best lecturers in the world. I have taken multiple of their free online courses including their previous years' CS50 course and cannot recommend it enough.&lt;/p&gt;

&lt;p&gt;Below is lecture number nine &lt;em&gt;(starting at index 0)&lt;/em&gt; of the course that focuses on HTML, CSS and JavaScript. It teaches you the fundamentals of how web pages work and introduces how the three languages work together to create interactive websites.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/fiR5QMZn3XA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Even if you already know HTML and CSS, you'll be surprised at how much you learn from these lectures by going back to basics and gaining a deeper understanding of the fundamentals.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript
&lt;/h3&gt;

&lt;p&gt;Picking up the basics of HTML and CSS is significantly easier than learning JavaScript; which is a behemoth in comparison; but also a lot more &lt;strong&gt;fun&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;First, you need to learn the absolute basics of &lt;strong&gt;programming&lt;/strong&gt; before diving deeper into JavaScript itself. This includes concepts key to any programming language; variables, functions, loops, and data structures.&lt;/p&gt;

&lt;p&gt;A relatively concise resource that will teach you these basic fundamentals is the below video, "JavaScript Tutorial For Beginners".&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/W6NZfCO5SIk"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;The video above is just an example of beginner-friendly content to understand the basics of JavaScript. There are plenty of others out there and I encourage you to learn as much as you need before advancing further into this roadmap.&lt;/p&gt;

&lt;p&gt;Here are some more great interactive resources that you can use to learn as a beginner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=PkZNo7MFNFg" rel="noopener noreferrer"&gt;Free Code Camp: Full JavaScript Course for Beginners&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.theodinproject.com/" rel="noopener noreferrer"&gt;The Odin Project&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://javascript30.com/" rel="noopener noreferrer"&gt;Wes Bos - JavaScript30&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/" rel="noopener noreferrer"&gt;MDN Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Optional (For Now): Computer Science Fundamentals
&lt;/h3&gt;

&lt;p&gt;JavaScript is beginner-friendly and abstracts away many complexities of programming; it's designed so that you can hit the ground running quickly.&lt;/p&gt;

&lt;p&gt;If you are willing to spend &lt;strong&gt;significantly&lt;/strong&gt; more time learning the fundamentals of computer science, consider watching the &lt;a href="https://www.youtube.com/playlist?list=PLhQjrBD2T380F_inVRXMIHCqLaNUd7bN4" rel="noopener noreferrer"&gt;full playlist of lectures for CS50 2022&lt;/a&gt;; which is ~20 hours total.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My personal opinion&lt;/em&gt; is that it's better to come back to these fundamentals later and &lt;strong&gt;start building something as soon as possible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I personally didn't learn these computer science fundamentals in detail until 1-2 years into working as a professional software engineer (which might sound crazy, I know). Again, these are my personal opinions and others will disagree with this.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Advanced JavaScript
&lt;/h3&gt;

&lt;p&gt;Here's where the learning curve reached a steep point for me.&lt;/p&gt;

&lt;p&gt;The resources I'm about to share may feel overwhelming at first; it took me several re-watches and many, many pages of notes to start to understand these concepts.&lt;/p&gt;
&lt;h4&gt;
  
  
  How JavaScript &lt;em&gt;Really Works&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;The below video goes deep into the nuances of JavaScript and how the magic of the language works under the hood! 🧙&lt;/p&gt;

&lt;p&gt;Topics like scope, closures, and prototypical inheritance are funnily enough the topics I've been asked about several times in job interviews over the past few years.&lt;/p&gt;

&lt;p&gt;This is another CS50 lecture from 2018, and is still one of my personal favourite resources for learning JavaScript to this day!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/X52b-8y2Hf4"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h4&gt;
  
  
  Asynchronous JavaScript
&lt;/h4&gt;

&lt;p&gt;The subsequent lecture in this playlist is also quite complex but covers incredibly important topics for understanding JavaScript at a deeper level. You'll be introduced to some of the more modern features of JavaScript introduced in &lt;a href="https://www.w3schools.com/js/js_es6.asp" rel="noopener noreferrer"&gt;ES6&lt;/a&gt; such as callbacks, promises, and asynchronous functions.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3Ay2lS6tm4M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  (Optional) Paid Course: "ES6 for Everyone!" by Wes Bos
&lt;/h4&gt;

&lt;p&gt;An additional resource I recommend (if you can afford it) is the "&lt;a href="https://es6.io/" rel="noopener noreferrer"&gt;ES6 for Everyone!&lt;/a&gt;" course by &lt;a href="https://twitter.com/wesbos" rel="noopener noreferrer"&gt;Wes Bos&lt;/a&gt;. It's an excellent interactive course that teaches you a variety of super helpful syntactic sugar that are part of the later releases of &lt;a href="https://en.wikipedia.org/wiki/ECMAScript" rel="noopener noreferrer"&gt;ECMAScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's not essential, but Wes is a great teacher and the course is a fun way to learn the more efficient ways of doing things in JavaScript.&lt;/p&gt;

&lt;p&gt;This course is very beginner friendly and will help you write more modern Javascript.&lt;/p&gt;

&lt;h4&gt;
  
  
  (VERY Optional) Paid Course: Master the Coding Interview
&lt;/h4&gt;

&lt;p&gt;As you're on your journey and are building your projects, some code you write is not going to be as efficient as it could be.&lt;/p&gt;

&lt;p&gt;A paid course that taught me a tremendous amount about writing better code is the "&lt;a href="https://www.udemy.com/course/master-the-coding-interview-big-tech-faang-interviews/" rel="noopener noreferrer"&gt;Master the Coding Interview: Big Tech (FAANG) Interviews&lt;/a&gt;" course by &lt;a href="https://twitter.com/andreineagoie" rel="noopener noreferrer"&gt;Andrei Neagoie&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The title of the course is a bit clickbaity, but it teaches you almost everything there is to learn about data structures and algorithms; with 30 interactive questions for you to go through; all in JavaScript!&lt;/p&gt;

&lt;p&gt;For me, this was extremely hard and took me many months to get through; which is why I put the "very optional" tag on this course. It's an amazing resource that I recommend you take at some point on your journey, but if you're a beginner it's likely going to be too hard for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  React - Building User Interfaces
&lt;/h2&gt;

&lt;p&gt;If you've started building your project by this point, you've probably realised that it is difficult to build an application by writing HTML, CSS, and JS alone.&lt;/p&gt;

&lt;p&gt;Developers have come up with many different libraries and frameworks to address these issues over the past decade; &lt;a href="https://jquery.com/" rel="noopener noreferrer"&gt;jQuery&lt;/a&gt;, &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;, &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;Svelte&lt;/a&gt;, &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;... the list goes on!&lt;/p&gt;

&lt;p&gt;One library has come out on top; &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;, which is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://survey.stackoverflow.co/2022/#web-frameworks-and-technologies" rel="noopener noreferrer"&gt;The most used frontend JS framework of 2022&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://survey.stackoverflow.co/2022/#web-frameworks-and-technologies" rel="noopener noreferrer"&gt;The framework that most developers "want" to use in 2022&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The most in-demand frontend framework for landing a job &lt;em&gt;(too lazy to get a source for this stat, so... "probably"&lt;/em&gt; 😄).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React uses a syntax called &lt;a href="https://reactjs.org/docs/introducing-jsx.html" rel="noopener noreferrer"&gt;JSX&lt;/a&gt; to combine writing your UI structure and design with your "interactivity" in the same location:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wuabocpaoelw3z63wfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wuabocpaoelw3z63wfk.png" alt="JSX Syntax preview" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A great resource to understand the "why" of React is this CS50W lecture on user interfaces:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/jrBhi8wbzPw"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  Get Started with Next.js
&lt;/h3&gt;

&lt;p&gt;Next.js documentation is absolutely brilliant. &lt;a href="https://twitter.com/leeerob" rel="noopener noreferrer"&gt;&lt;strong&gt;Lee Robinson&lt;/strong&gt;&lt;/a&gt; and his team of DX engineers have written fantastic, interactive documentation to help you learn Next.js and quiz your knowledge along the way.&lt;/p&gt;

&lt;p&gt;For this reason, I recommend running through the interactive documentation first; and then diving into &lt;a href="https://nextjs.org/docs#automatic-setup" rel="noopener noreferrer"&gt;creating your first Next.js application with their CLI&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  TypeScript
&lt;/h2&gt;

&lt;p&gt;Another hot take on this article 🌶️: Start using TypeScript ASAP! JavaScript is a great language for beginners, but this is a double-edged sword. Part of the reason it's good for beginners is that it gives you a lot of freedom to write 💩 code and it doesn't complain about it!&lt;/p&gt;

&lt;p&gt;TypeScript can be a real pain in the 🍑 at first, but once it clicks, you'll never want to go back. More and more developers and developer tools are defaulting to TypeScript and for good reason; type safety makes your code significantly less likely to have bugs!&lt;/p&gt;

&lt;p&gt;My suggestion: turn &lt;a href="https://www.typescriptlang.org/tsconfig#strict" rel="noopener noreferrer"&gt;strict mode&lt;/a&gt; off at the beginning. Use the &lt;a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any" rel="noopener noreferrer"&gt;&lt;code&gt;any&lt;/code&gt;&lt;/a&gt; type whenever you get completely stuck and don't know what to do. These are bad practices in reality, but will help you onboard to TypeScript without wanting to break your keyboard.&lt;/p&gt;

&lt;p&gt;As you write more and more code with TypeScript and pick up more tricks, you'll feel comfortable enabling strict mode and using the &lt;code&gt;any&lt;/code&gt; type more sparingly; until you eventually have complete type safety in your codebase and look at JavaScript in disgust!&lt;/p&gt;
&lt;h2&gt;
  
  
  Styling
&lt;/h2&gt;

&lt;p&gt;There are a number of options for you to build out more beautiful applications that are ultimately just different ways of abstracting CSS so that it's ... well basically CSS but easier.&lt;/p&gt;

&lt;p&gt;The general consensus going into 2023 is that &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; is the gold standard for adding styles to your application. I personally like to use a tool called &lt;a href="https://mui.com/" rel="noopener noreferrer"&gt;Material UI&lt;/a&gt; that comes with pre-built components; however, most people don't agree with me on this.&lt;/p&gt;

&lt;p&gt;A great video weighing up the different options available and their pros and cons is "&lt;a href="https://www.youtube.com/watch?v=CQuTF-bkOgc" rel="noopener noreferrer"&gt;The Best of CSS&lt;/a&gt;" by &lt;a href="https://twitter.com/t3dotgg" rel="noopener noreferrer"&gt;Theo&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CQuTF-bkOgc"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;Short answer: If you don't already know and love a UI library, pick Tailwind CSS.&lt;/p&gt;
&lt;h2&gt;
  
  
  Blockchain Fundamentals
&lt;/h2&gt;

&lt;p&gt;If you don't already have knowledge of what a blockchain or smart contract is, a helpful resource for beginners is &lt;a href="https://twitter.com/PatrickAlphaC" rel="noopener noreferrer"&gt;Patrick Collins&lt;/a&gt;' blockchain course.&lt;/p&gt;

&lt;p&gt;I suggest you watch the &lt;strong&gt;first 2 hours&lt;/strong&gt; of the 32-hour course which introduces the core concepts of blockchain, smart contracts and web3. You can pick and choose the other areas of the course that interest you as you wish!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/gyMwXuJrbJQ"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Web3 Stack
&lt;/h2&gt;

&lt;p&gt;An introductory video I recommend watching is "Defining the Web3 Stack", by @&lt;a href="https://dev.to@dabit3"&gt;Nader Dabit&lt;/a&gt;; which outlines the equivalent web3 tech stack compared to a traditional "web2" application.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/f9XRH7bjV8M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As an updated graphic to Nader's talk in 2021, I have provided my thoughts on the tech stack of a web3 application below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pb7767u9ew1bs3n557x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pb7767u9ew1bs3n557x.png" alt="Full tech stack diagram for web3" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of moving pieces to consider when building a web3 application. There are a plethora of tools available to help you build different aspects of your decentralized application, and it's up to you to learn what you like best.&lt;/p&gt;

&lt;p&gt;Of course, I'll provide my recommendations here that I have come to after experimenting with a number of these tools in the past year.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a full stack web3 app "the hard way"
&lt;/h3&gt;

&lt;p&gt;Arguably the best resource I consumed when I was entering the web3 space was @&lt;a href="https://dev.to@dabit3"&gt;Nader Dabit&lt;/a&gt;'s "&lt;a href="https://www.youtube.com/watch?v=GKJBEEXUha0" rel="noopener noreferrer"&gt;Full Stack NFT Marketplace&lt;/a&gt;" video tutorial. You'll learn how to build almost every aspect of a full-stack web3 application at a rapid pace, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;NFT Collection smart contract&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NFT marketplace smart contract&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Smart contract development environment and testing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IPFS file uploads and downloads&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frontend technologies &amp;amp; Next.js to build a web3 application&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RPC nodes and IPFS Gateways&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GKJBEEXUha0"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;This might feel like quite the jump from the previous step, and you might not understand everything in this video immediately, and that's okay!&lt;/p&gt;

&lt;p&gt;This resource taught me a tremendous amount about how the pieces of the web3 stack fit together and teach you how to build an epic project with modern technologies.&lt;/p&gt;
&lt;h4&gt;
  
  
  Extending Nader's Project
&lt;/h4&gt;

&lt;p&gt;Something that &lt;em&gt;really&lt;/em&gt; made me understand what was happening was adding more functionality to Nader's NFT marketplace project; specifically, adding a function to get an individual listing's information on the marketplace smart contract.&lt;/p&gt;

&lt;p&gt;Adding this capability to the project might sound easy, but it tests to see if you really understand how the project is functioning; and is a great step to add on top of the build itself.&lt;/p&gt;
&lt;h4&gt;
  
  
  Why is this "the hard way"?
&lt;/h4&gt;

&lt;p&gt;This resource introduces you to what I'd argue is a low-level abstraction of building a full-stack web3 app; it shows you every step of the process which is super important to understand.&lt;/p&gt;

&lt;p&gt;However, I'd argue that the purpose of the video isn't to build a "production-ready" application in 2023. I say this because there are a large number of tools that have been released since mid-2021 &lt;em&gt;(the time this video was made),&lt;/em&gt; that has abstracted away the complexity of building full-stack web3 applications significantly.&lt;/p&gt;

&lt;p&gt;Let's quickly cover a few other core concepts and then dive into the more modern tools available to build web3 apps with.&lt;/p&gt;
&lt;h2&gt;
  
  
  RPC Nodes
&lt;/h2&gt;

&lt;p&gt;To communicate to and from the blockchain, you need to use a node. Unless you want to run your own Node, you need to use a service provider such as Alchemy, Coinbase, or Moralis, &lt;em&gt;(or thirdweb as we'll explain in the next section)&lt;/em&gt; to do so.&lt;/p&gt;

&lt;p&gt;Below is a great resource outlining what an RPC Node is and why it's required to build web3 applications.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.alchemy.com/overviews/rpc-node" rel="noopener noreferrer"&gt;
      alchemy.com
    &lt;/a&gt;
&lt;/div&gt;
 
&lt;h4&gt;
  
  
  Interesting Project: Lava Network
&lt;/h4&gt;

&lt;p&gt;A project I am excited to see in 2023 is &lt;a href="https://lavanet.xyz/" rel="noopener noreferrer"&gt;Lava Network&lt;/a&gt;; which offers a decentralized alternative to the RPC Nodes. Check it out if you are interested!&lt;/p&gt;
&lt;h2&gt;
  
  
  Decentralized Storage Solutions
&lt;/h2&gt;

&lt;p&gt;Not all information needs to be stored on the blockchain. Storing data on the blockchain itself costs gas fees and has a significant storage limit; for this reason, a common pattern is to store your information "off-chain" and store the URL of that data on the blockchain instead.&lt;/p&gt;

&lt;p&gt;Storage solutions exist that aren't stored "on-chain" but are still not controlled by a centralized service such as AWS or Google Cloud. The two prominent decentalized storage solutions solutions in 2023 are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://ipfs.tech/" rel="noopener noreferrer"&gt;IPFS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.arweave.org/" rel="noopener noreferrer"&gt;Arweave&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  IPFS
&lt;/h3&gt;

&lt;p&gt;Below is a concise summary of what IPFS is, how it works, and how you can integrate it into your applications using &lt;a href="https://thirdweb.com/storage" rel="noopener noreferrer"&gt;thirdweb&lt;/a&gt; (we'll talk more about this next).&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4Nnu9Cy7SKc"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;To access data stored on IPFS on most browsers such as Google Chrome, you'll need to use an IPFS Gateway. There are a couple of options available for you here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Public gateway (available for everyone, not very reliable)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Private gateways - using services such as &lt;a href="https://www.pinata.cloud/" rel="noopener noreferrer"&gt;Pinata&lt;/a&gt; or &lt;a href="http://nft.storage" rel="noopener noreferrer"&gt;nft.storage&lt;/a&gt; for faster speed and higher availability, &lt;em&gt;(again, or just use thirdweb as we discuss next)&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Arweave
&lt;/h3&gt;

&lt;p&gt;Another excellent video by Nader Dabit is available for you to understand Arweave and other aspects of the Arweave ecosystem such as &lt;a href="https://github.com/ArweaveTeam/SmartWeave" rel="noopener noreferrer"&gt;Smartweave&lt;/a&gt; and &lt;a href="https://warp.cc/" rel="noopener noreferrer"&gt;Warp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/cGLMN5A2C4E"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;I should mention a lot of these technologies such as Arweave have &lt;a href="https://www.arweave.org/get-involved/investment-funding" rel="noopener noreferrer"&gt;grant programs&lt;/a&gt; if you are building with them; if you're looking for some funding this might be interesting to you.&lt;/p&gt;
&lt;h2&gt;
  
  
  thirdweb
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Full disclosure: I work on the&lt;/strong&gt; &lt;a href="https://twitter.com/thirdweb" rel="noopener noreferrer"&gt;&lt;strong&gt;thirdweb&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;team!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used Nader's NFT marketplace video as a starting point for a freelancing project I was working on as I began to increase my skills in web3 development. I came to the realisation that building smart contracts is HARD!&lt;/p&gt;

&lt;p&gt;I didn't have the confidence at this point to write a fully-featured, gas-optimized, bug-free marketplace smart contract for my clients' requirements; so I started to ask myself: &lt;em&gt;"someone must have done this and open-sourced it somewhere RIGHT!?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Well, turns out I was right!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I stumbled upon &lt;a href="https://thirdweb.com/" rel="noopener noreferrer"&gt;thirdweb&lt;/a&gt; shortly after it was released and discovered they had open-source smart contracts for NFT collections, marketplaces, and more.&lt;/p&gt;

&lt;p&gt;I basically planned to just copy-paste their solidity smart contracts into my existing project and carry on my way 😂! &lt;em&gt;Here's proof (don't tell&lt;/em&gt; &lt;a href="https://twitter.com/FurqanR" rel="noopener noreferrer"&gt;&lt;em&gt;Furqan&lt;/em&gt;&lt;/a&gt;&lt;em&gt;):&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw59x3gzwtk52h2xb0hm3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw59x3gzwtk52h2xb0hm3.png" alt="Screenshot of me in thirdweb discord" width="454" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But as I explored the product more, I discovered they had an SDK! I didn't have to use ethers anymore! They also have tools to connect users' wallets, and manage my IPFS storage for me! I didn't even need my smart contract code in my project anymore as they had a dashboard I could deploy it with.&lt;/p&gt;

&lt;p&gt;I was able to remove SO MUCH of the code I wrote for my marketplace project and replace them with thirdweb tools that I ended up falling in love with the product; &lt;em&gt;and applied for my current position at thirdweb a few months after completing my freelance work!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So, while I do get paid to advocate thirdweb's products, I do it from my heart, not from my ... wallet?&lt;/em&gt; 🥲 I was really out here using their tools before I joined the team.&lt;/p&gt;
&lt;h3&gt;
  
  
  Learning thirdweb
&lt;/h3&gt;

&lt;p&gt;I've spent a lot of time refining the &lt;a href="https://portal.thirdweb.com/getting-started" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt; flow of the thirdweb documentation to get you up and running from building a smart contract to building a front-end Next.js application in no time! So, my recommendation is to start there!&lt;/p&gt;

&lt;p&gt;Alternatively, there is a getting started video that covers the same content:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/fzsz-b2JV9U"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;Once you get the power of thirdweb's stack, you won't want to go back! 🤠 Since I joined, we have shipped even more features that make the web3 building process simpler:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Free IPFS gateway and IPFS rendering components in the SDK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Free RPC included directly in the SDK (which can be overridden).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IPFS upload and downloads in React hooks with &lt;a href="https://portal.thirdweb.com/storage" rel="noopener noreferrer"&gt;Storage&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://portal.thirdweb.com/react" rel="noopener noreferrer"&gt;React hooks&lt;/a&gt; for everything you can imagine on the frontend; for both user wallets and interacting with smart contracts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Full Solidity SDK called "&lt;a href="https://portal.thirdweb.com/contractkit" rel="noopener noreferrer"&gt;ContractKit&lt;/a&gt;" which is similar to OpenZeppelin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An &lt;a href="https://thirdweb.com/explore" rel="noopener noreferrer"&gt;Explore&lt;/a&gt; page; for you to deploy the most popular smart contract standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Much more!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deploy a smart contract with thirdweb and connect to it in your Next.js application and I promise you'll never want to go back to your old stack.&lt;/p&gt;
&lt;h2&gt;
  
  
  Other Web3 Frontend Libraries
&lt;/h2&gt;

&lt;p&gt;So that I'm not completely biased, I'll also recommend you some of the other modern libraries available for you to build more robust frontends for your web3 apps.&lt;/p&gt;
&lt;h3&gt;
  
  
  wagmi
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://wagmi.sh/" rel="noopener noreferrer"&gt;wagmi&lt;/a&gt; is a library of React Hooks for building EVM frontends. Much like the &lt;a href="https://portal.thirdweb.com/react" rel="noopener noreferrer"&gt;thirdweb React SDK&lt;/a&gt;, it has hooks for pretty much everything you can imagine and is significantly better than using ethers or web3.js alone.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://wagmi.sh/core/getting-started" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; is a great starting point for you to understand how to use it in your Next.js/React apps.&lt;/p&gt;
&lt;h3&gt;
  
  
  Rainbow Kit
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.rainbowkit.com/" rel="noopener noreferrer"&gt;RainbowKit&lt;/a&gt; is an excellent library for adding connect wallet capabilities into your application and supporting a variety of different connection options for your users.&lt;/p&gt;

&lt;p&gt;Their &lt;a href="https://www.rainbowkit.com/docs/introduction" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; is concise and includes helpful short videos to get you up and running quickly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Indexing Solutions
&lt;/h2&gt;

&lt;p&gt;Some web3 applications require information that isn't directly available from any smart contract; such as what NFTs a wallet owns, or how many times an NFT has been transferred.&lt;/p&gt;

&lt;p&gt;When it comes to indexing solutions, you can use &lt;a href="https://thegraph.com/en/" rel="noopener noreferrer"&gt;The Graph&lt;/a&gt;; a decentralized solution, and again use Nader's resources to learn how to get started:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/JpOLhkmtOak"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;You might prefer to use SDKs or API endpoints to get your indexed information, and there are a number of popular solutions you can use, each with helpful resources and videos on their documentation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.alchemy.com/sdk" rel="noopener noreferrer"&gt;Alchemy&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.cloud.coinbase.com/node/docs/welcome-to-node" rel="noopener noreferrer"&gt;Coinbase Cloud&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://moralis.io/" rel="noopener noreferrer"&gt;Moralis&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools have similar capabilities with various pros and cons, you can use their documentation to explore which tool is right for you.&lt;/p&gt;
&lt;h2&gt;
  
  
  Smart Contracts
&lt;/h2&gt;

&lt;p&gt;This might seem crazy that the smart contracts section is all the way down here. 🤯&lt;/p&gt;

&lt;p&gt;The reason I placed smart contracts after learning the full frontend tech stack is that you are often going to be building on top of smart contracts that &lt;em&gt;other&lt;/em&gt; people have deployed already; such as &lt;a href="https://www.lens.xyz/" rel="noopener noreferrer"&gt;Lens Protocol&lt;/a&gt;, or deploying gas-optimized, audited smart contracts that suit your needs such as those available on &lt;a href="https://thirdweb.com/explore" rel="noopener noreferrer"&gt;thirdweb Explore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To land a job in web3 you don't need to be an expert in writing your own smart contracts. At thirdweb, we have a team that is dedicated to writing Solidity smart contracts and building out our Solidity SDK; the rest of our engineers &lt;em&gt;understand&lt;/em&gt; the Solidity code; but aren't actively writing smart contracts on a daily basis.&lt;/p&gt;

&lt;p&gt;This might be a hot take 🌶️🥵 but you should learn the details of building smart contracts &lt;em&gt;after&lt;/em&gt; learning how to actually &lt;strong&gt;build stuff people interact with&lt;/strong&gt;; which &lt;em&gt;usually&lt;/em&gt; means you need some kind of frontend application skills.&lt;/p&gt;

&lt;p&gt;Some people will disagree with that; as almost everything in web3 depends on the foundational layer of decentralized applications; &lt;strong&gt;which is smart contracts&lt;/strong&gt;. I'm not saying it's correct, this is just the path that I personally took to learn web3.&lt;/p&gt;

&lt;p&gt;There are many resources you can use to learn Solidity itself, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://cryptozombies.io/" rel="noopener noreferrer"&gt;CryptoZombies&lt;/a&gt; (interactive course)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Literally, just read the &lt;a href="https://docs.soliditylang.org/en/v0.8.17/" rel="noopener noreferrer"&gt;Solidity documentation&lt;/a&gt; 🥸&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Object Oriented Programming
&lt;/h3&gt;

&lt;p&gt;OOP. &lt;em&gt;more like oof&lt;/em&gt;, am I right? 😑&lt;/p&gt;

&lt;p&gt;Solidity is an object-oriented programming language to build your own smart contracts. If you know OOP principles you are already most of the way there to be able to write basic Solidity.&lt;/p&gt;

&lt;p&gt;Usually, you don't see JavaScript used for object-oriented programming; other languages like Java, C#, or Python are better suited for this style of writing code.&lt;/p&gt;

&lt;p&gt;To get the basics of OOP down, I recommend you consume as many resources as you need to to get the basic principles down. Below is an introduction to OOP in JavaScript that will help you get the core concepts:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/PFmuCDHHpwk"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h4&gt;
  
  
  OOP In Solidity
&lt;/h4&gt;

&lt;p&gt;Once you know what OOP is, I recommend you return back to Patrick's video at &lt;a href="https://youtu.be/gyMwXuJrbJQ?t=11135" rel="noopener noreferrer"&gt;this timestamp&lt;/a&gt; (chapter 3), to see how inheritance and overrides work in Solidity:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/watch?t=11135&amp;amp;v=gyMwXuJrbJQ&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;
 
&lt;h3&gt;
  
  
  Using OSS Smart Contracts
&lt;/h3&gt;

&lt;p&gt;Usually, you'll want to build some variety of ERC standards such as ERC20 fungible tokens, ERC721 NFTs, or marketplaces, with a twist that makes your project unique.&lt;/p&gt;

&lt;p&gt;Now that you know how to utilize inheritance in Solidity, you can make use of libraries such as &lt;a href="https://www.openzeppelin.com/contracts" rel="noopener noreferrer"&gt;OpenZeppelin Contracts&lt;/a&gt; and &lt;a href="https://portal.thirdweb.com/contractkit" rel="noopener noreferrer"&gt;thirdweb ContractKit&lt;/a&gt; to import core chunks of logic into your smart contracts; and customize them as you wish!&lt;/p&gt;
&lt;h4&gt;
  
  
  ContractKit
&lt;/h4&gt;

&lt;p&gt;thirdweb's ContractKit has a vast array of plug-and-play smart contracts for you to extend, customize, and include in your own smart contracts; including ERC721, ERC1155, ERC20, staking, airdrop, and more 🤠!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/G3IHeKhVtpQ"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h3&gt;
  
  
  Smart Contract Security
&lt;/h3&gt;

&lt;p&gt;It is important to know the most common vulnerabilities of smart contracts, which again Patrick covers thoroughly in his 32-hour Solidity course in Chapter 18 at &lt;a href="https://youtu.be/gyMwXuJrbJQ?t=113313" rel="noopener noreferrer"&gt;this timestamp&lt;/a&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://www.youtube.com/watch?t=113313&amp;amp;v=gyMwXuJrbJQ&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;
      youtube.com
    &lt;/a&gt;
&lt;/div&gt;
 

&lt;p&gt;There is also a detailed &lt;a href="https://github.com/kadenzipfel/smart-contract-attack-vectors" rel="noopener noreferrer"&gt;open-source repository&lt;/a&gt; outlining common attack vectors for smart contracts and historical exploits using these attacks:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kadenzipfel" rel="noopener noreferrer"&gt;
        kadenzipfel
      &lt;/a&gt; / &lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities" rel="noopener noreferrer"&gt;
        smart-contract-vulnerabilities
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A collection of smart contract vulnerabilities along with prevention methods
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Smart Contract Vulnerabilities&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A collection of smart contract vulnerabilities along with prevention methods&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Access Control&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/authorization-txorigin.md" rel="noopener noreferrer"&gt;Authorization Through tx.origin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/insufficient-access-control.md" rel="noopener noreferrer"&gt;Insufficient Access Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/delegatecall-untrusted-callee.md" rel="noopener noreferrer"&gt;Delegatecall to Untrusted Callee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/signature-malleability.md" rel="noopener noreferrer"&gt;Signature Malleability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/missing-protection-signature-replay.md" rel="noopener noreferrer"&gt;Missing Protection against Signature Replay Attacks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Math&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/overflow-underflow.md" rel="noopener noreferrer"&gt;Integer Overflow and Underflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/off-by-one.md" rel="noopener noreferrer"&gt;Off-By-One&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/lack-of-precision.md" rel="noopener noreferrer"&gt;Lack of Precision&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Control Flow&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/reentrancy.md" rel="noopener noreferrer"&gt;Reentrancy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/dos-gas-limit.md" rel="noopener noreferrer"&gt;DoS with Block Gas Limit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/dos-revert.md" rel="noopener noreferrer"&gt;DoS with (Unexpected) revert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/msgvalue-loop.md" rel="noopener noreferrer"&gt;Using &lt;code&gt;msg.value&lt;/code&gt; in a Loop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/transaction-ordering-dependence.md" rel="noopener noreferrer"&gt;Transaction-Ordering Dependence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/insufficient-gas-griefing.md" rel="noopener noreferrer"&gt;Insufficient Gas Griefing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Data Handling&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/unchecked-return-values.md" rel="noopener noreferrer"&gt;Unchecked Return Value&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/arbitrary-storage-location.md" rel="noopener noreferrer"&gt;Write to Arbitrary Storage Location&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/unbounded-return-data.md" rel="noopener noreferrer"&gt;Unbounded Return Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/uninitialized-storage-pointer.md" rel="noopener noreferrer"&gt;Uninitialized Storage Pointer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/unexpected-ecrecover-null-address.md" rel="noopener noreferrer"&gt;Unexpected &lt;code&gt;ecrecover&lt;/code&gt; null address&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Unsafe Logic&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/weak-sources-randomness.md" rel="noopener noreferrer"&gt;Weak Sources of Randomness from Chain Attributes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/hash-collision.md" rel="noopener noreferrer"&gt;Hash Collision when using abi.encodePacked() with Multiple Variable-Length Arguments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/timestamp-dependence.md" rel="noopener noreferrer"&gt;Timestamp Dependence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/unsafe-low-level-call.md" rel="noopener noreferrer"&gt;Unsafe Low-Level Call&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/unsupported-opcodes.md" rel="noopener noreferrer"&gt;Unsupported Opcodes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/unencrypted-private-data-on-chain.md" rel="noopener noreferrer"&gt;Unencrypted Private Data On-Chain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/asserting-contract-from-code-size.md" rel="noopener noreferrer"&gt;Asserting Contract from Code Size&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Code Quality&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/floating-pragma.md" rel="noopener noreferrer"&gt;Floating Pragma&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/outdated-compiler-version.md" rel="noopener noreferrer"&gt;Outdated Compiler Version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/use-of-deprecated-functions.md" rel="noopener noreferrer"&gt;Use of Deprecated Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/incorrect-constructor.md" rel="noopener noreferrer"&gt;Incorrect Constructor Name&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/shadowing-state-variables.md" rel="noopener noreferrer"&gt;Shadowing State Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/incorrect-inheritance-order.md" rel="noopener noreferrer"&gt;Incorrect Inheritance Order&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/unused-variables.md" rel="noopener noreferrer"&gt;Presence of Unused Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/default-visibility.md" rel="noopener noreferrer"&gt;Default Visibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/inadherence-to-standards.md" rel="noopener noreferrer"&gt;Inadherence to Standards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/assert-violation.md" rel="noopener noreferrer"&gt;Assert Violation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/kadenzipfel/smart-contract-vulnerabilities./vulnerabilities/requirement-violation.md" rel="noopener noreferrer"&gt;Requirement&lt;/a&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kadenzipfel/smart-contract-vulnerabilities" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
 
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it! These are the resources I personally used to develop my knowledge in web3 and land a job as a developer relations engineer at &lt;a href="https://twitter.com/thirdweb" rel="noopener noreferrer"&gt;thirdweb&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I'd like to reiterate that tutorials and resources alone will never be enough to become a dev. You need to get your hands dirty ASAP, come back to these resources as you run into problems in your project, and constantly fight battles on your own over many years to come.&lt;/p&gt;

&lt;p&gt;The goal of this article isn't to give you months of resources to consume, it's here so you can come back to trusted sources of high-quality information as you build your own projects alone! 🦸&lt;/p&gt;

&lt;p&gt;If you want to join a community of devs on your journey, jump into my community &lt;a href="https://discord.com/invite/4eQBm7DDNS" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; and introduce yourself! 👋&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://discord.com/invite/4eQBm7DDNS" rel="noopener noreferrer"&gt;
      discord.com
    &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>Create a Discord Bot that Assigns a Role to NFT Holders</title>
      <dc:creator>Jarrod Watts</dc:creator>
      <pubDate>Mon, 25 Jul 2022 06:25:21 +0000</pubDate>
      <link>https://dev.to/thirdweb/create-a-discord-bot-that-assigns-a-role-to-nft-holders-50k1</link>
      <guid>https://dev.to/thirdweb/create-a-discord-bot-that-assigns-a-role-to-nft-holders-50k1</guid>
      <description>&lt;p&gt;In this guide, we'll set up a Discord bot that checks if a wallet has an NFT from a collection, and grants them a special role on our Discord server if they do!&lt;/p&gt;

&lt;p&gt;Similar to &lt;a href="https://collab.land/"&gt;Collab.Land&lt;/a&gt;, we'll ask the user to sign in with their wallet as well as their Discord account on our web application, and ask a bot we create to grant them a role on our server using the Discord API running on a Next.js API route.&lt;/p&gt;

&lt;p&gt;You can grab the source code for this project using this link: &lt;a href="https://github.com/thirdweb-example/discord-role-granter"&gt;https://github.com/thirdweb-example/discord-role-granter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's do it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a thirdweb app
&lt;/h2&gt;

&lt;p&gt;To get started, we can use the &lt;a href="https://portal.thirdweb.com/thirdweb-cli?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=discord-role-granter"&gt;thirdweb CLI&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx thirdweb create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll be using TypeScript and Next.js for this guide; so give your app a name and select &lt;strong&gt;Next.js&lt;/strong&gt; for the framework, and &lt;strong&gt;TypeScript&lt;/strong&gt; for the language.&lt;/p&gt;

&lt;p&gt;For this guide, we'll assume you already have a Discord server created and a role set up in the server. If you don't have one, go ahead and create one now and come back to this guide, because next up, we'll create a bot and invite it to our server!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating A Discord Bot
&lt;/h2&gt;

&lt;p&gt;To create a Discord bot, head to the &lt;a href="https://discord.com/developers/applications"&gt;Discord Developer Portal&lt;/a&gt; and click on &lt;code&gt;New Application&lt;/code&gt;, give it a name and click &lt;code&gt;create&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hra5WPOD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378066493/uTAi0N7I-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hra5WPOD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378066493/uTAi0N7I-.png" alt="image.png" width="466" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it's created, head to the &lt;code&gt;Bot&lt;/code&gt; tab, and click &lt;code&gt;Add Bot&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sUiP16ch--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378135064/eGNkkLpf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sUiP16ch--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378135064/eGNkkLpf3.png" alt="image.png" width="483" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your bot a username, and I'm unchecking the &lt;code&gt;Public Bot&lt;/code&gt; field so that only we can invite our bot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--toq4V0Xk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378382494/i5srin_Du.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--toq4V0Xk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378382494/i5srin_Du.png" alt="image.png" width="880" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to &lt;code&gt;Bot Permissions&lt;/code&gt; and give our bot the &lt;code&gt;Manage Roles&lt;/code&gt; permission:&lt;/p&gt;

&lt;p&gt;It's important to note that you should only give your bot the roles it requires. If your bot token is compromised, other users can perform any actions you have permitted it to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iqlNvBD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378279649/0SCOzdku8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iqlNvBD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378279649/0SCOzdku8.png" alt="image.png" width="880" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you're ready, click &lt;code&gt;Save Changes&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Now we're ready to invite our bot to our server!&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;OAuth2&lt;/code&gt; &amp;gt; &lt;code&gt;URL Generator&lt;/code&gt; on the sidebar:&lt;/p&gt;

&lt;p&gt;Select &lt;code&gt;bot&lt;/code&gt; and &lt;code&gt;Manage Roles&lt;/code&gt; scopes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OQYrLjVc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378764505/Q6hmuH8Ml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OQYrLjVc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378764505/Q6hmuH8Ml.png" alt="image.png" width="880" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the Generated URL and open it in your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4HbyBGee--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378817253/bzEMllDkE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4HbyBGee--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378817253/bzEMllDkE.png" alt="image.png" width="880" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure it is the bot you expect, select the server you want to add your bot to and click &lt;code&gt;Continue&lt;/code&gt;. It will ask you to approve this bot's permissions, you should see a prompt to authorize the bot for &lt;code&gt;Manage Roles&lt;/code&gt; permissions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4vdsLwO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378873543/clVZw5YdI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4vdsLwO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378873543/clVZw5YdI.png" alt="image.png" width="398" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Authorise&lt;/code&gt;, once successful, you'll see an &lt;code&gt;Authorised&lt;/code&gt; window&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--23-7CKSo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378937976/yuvDcAWEG.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--23-7CKSo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378937976/yuvDcAWEG.png" alt="image.png" width="558" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And your bot will be added to your server - say hi!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BtA8GEeT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378948085/jchIC5F1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BtA8GEeT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658378948085/jchIC5F1o.png" alt="image.png" width="497" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticating Users
&lt;/h2&gt;

&lt;p&gt;To authenticate users with Discord, we'll be using the library `&lt;a href="https://next-auth.js.org/"&gt;NextAuth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a new folder inside of &lt;code&gt;pages&lt;/code&gt; called &lt;code&gt;api&lt;/code&gt;, and within that, create another folder called &lt;code&gt;auth&lt;/code&gt;, and within this &lt;code&gt;auth&lt;/code&gt; folder, create a file called &lt;code&gt;[...nextauth].ts&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
yarn add next-auth&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is where we'll configure our NextAuth setup and allow people to sign in to our application using Discord.&lt;/p&gt;

&lt;p&gt;Back in your Discord Developer Portal, copy across your &lt;code&gt;Client ID&lt;/code&gt; and &lt;code&gt;Client Secret&lt;/code&gt; into environment variables in your project, by creating a &lt;code&gt;.env.local&lt;/code&gt; file at the root of the directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qc53dKyM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658380648801/1rp4zc2-a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qc53dKyM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658380648801/1rp4zc2-a.png" alt="image.png" width="880" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;text&lt;br&gt;
CLIENT_ID=xxxxx&lt;br&gt;
CLIENT_SECRET=xxxxx&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We also need to add a Redirect URL into our Application while we're here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tExQAOlo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658443956419/84fGElQk0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tExQAOlo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658443956419/84fGElQk0.png" alt="image.png" width="880" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's make our &lt;code&gt;[...nextauth]&lt;/code&gt; API route look like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
import NextAuth from "next-auth";&lt;br&gt;
import DiscordProvider from "next-auth/providers/discord";&lt;br&gt;
export default NextAuth({&lt;br&gt;
  // Configure one or more authentication providers&lt;br&gt;
  providers: [&lt;br&gt;
    DiscordProvider({&lt;br&gt;
      clientId: process.env.CLIENT_ID as string,&lt;br&gt;
      clientSecret: process.env.CLIENT_SECRET as string,&lt;br&gt;
    }),&lt;br&gt;
  ],&lt;/p&gt;

&lt;p&gt;// When the user signs in, get their token&lt;br&gt;
  callbacks: {&lt;br&gt;
    async jwt({ token, account }) {&lt;br&gt;
      // Persist the OAuth access_token to the token right after signin&lt;br&gt;
      if (account) {&lt;br&gt;
        token.userId = account.providerAccountId;&lt;br&gt;
      }&lt;br&gt;
      return token;&lt;br&gt;
    },&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async session({ session, token, user }) {
  // Send properties to the client, like an access_token from a provider.
  session.userId = token.userId;
  return session;
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;},&lt;br&gt;
});&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You might notice we have some modifications to the data that gets returned inside these callbacks. We're doing this to add the user's ID into the token that gets returned by NextAuth, because we want to access that value when we try and grant this user the role (we'll need their ID).&lt;/p&gt;

&lt;p&gt;To access NextAuth's hooks such as &lt;code&gt;signIn&lt;/code&gt;, we need to wrap our application in the &lt;code&gt;SessionProvider&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`jsx&lt;br&gt;
import type { AppProps } from "next/app";&lt;br&gt;
import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";&lt;br&gt;
import { SessionProvider } from "next-auth/react";&lt;br&gt;
import "../styles/globals.css";&lt;/p&gt;

&lt;p&gt;// This is the chainId your dApp will work on.&lt;br&gt;
const activeChainId = ChainId.Mumbai;&lt;/p&gt;

&lt;p&gt;function MyApp({ Component, pageProps }: AppProps) {&lt;br&gt;
  return (&lt;br&gt;
    &lt;br&gt;
      &lt;br&gt;
        &lt;br&gt;
      &lt;br&gt;
    &lt;br&gt;
  );&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;export default MyApp;&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we're ready for users to authenticate to our application using Discord!&lt;/p&gt;

&lt;p&gt;Let's create a folder called &lt;code&gt;components&lt;/code&gt; and create a &lt;code&gt;SignIn.tsx&lt;/code&gt; component within that folder. This component will ask the user to both:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign In With Their Wallet&lt;/li&gt;
&lt;li&gt;Authenticate With Discord&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this component, three states can occur:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user is connected to both &lt;code&gt;wallet&lt;/code&gt; and &lt;code&gt;Discord&lt;/code&gt; =&amp;gt; We show them the main page.&lt;/li&gt;
&lt;li&gt;The user is not connected to &lt;code&gt;wallet&lt;/code&gt; =&amp;gt; We ask them to connect their wallet.&lt;/li&gt;
&lt;li&gt;The user is not connected to &lt;code&gt;Discord&lt;/code&gt;=&amp;gt; We ask them to authenticate with Discord.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the user is in state &lt;code&gt;1&lt;/code&gt;, (has both wallet connected and Discord connected), we can show them a button that will run some code on our server to check if they own an NFT. If they do own the NFT, our Discord bot will assign them a role in our Discord server!&lt;/p&gt;

&lt;p&gt;Let's write the code for these three states:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imports and hook definitions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`jsx&lt;br&gt;
import { useAddress, useDisconnect, useMetamask } from "@thirdweb-dev/react";&lt;br&gt;
import { useSession, signIn, signOut } from "next-auth/react";&lt;br&gt;
import React from "react";&lt;br&gt;
import styles from "../styles/Home.module.css";&lt;/p&gt;

&lt;p&gt;export default function SignIn() {&lt;br&gt;
  const address = useAddress();&lt;br&gt;
  const connectWithMetamask = useMetamask();&lt;br&gt;
  const disconnectWallet = useDisconnect();&lt;br&gt;
  const { data: session } = useSession();&lt;/p&gt;

&lt;p&gt;// rest of the code here&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State 1 - Both Wallet + Discord Connected&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;jsx&lt;br&gt;
// 1. The user is signed into discord and connected to wallet.&lt;br&gt;
if (session &amp;amp;&amp;amp; address) {&lt;br&gt;
  return (&lt;br&gt;
    &amp;lt;div className={styles.bigSpacerTop}&amp;gt;&lt;br&gt;
      &amp;lt;a onClick={() =&amp;gt; signOut()} className={styles.secondaryButton}&amp;gt;&lt;br&gt;
        Sign out of Discord&lt;br&gt;
      &amp;lt;/a&amp;gt;&lt;br&gt;
      |&amp;lt;a onClick={() =&amp;gt; disconnectWallet()} className={styles.secondaryButton}&amp;gt;&lt;br&gt;
        Disconnect wallet&lt;br&gt;
      &amp;lt;/a&amp;gt;&lt;br&gt;
    &amp;lt;/div&amp;gt;&lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State 2 - Connect Wallet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;jsx&lt;br&gt;
// 2. Connect Wallet&lt;br&gt;
if (!address) {&lt;br&gt;
  return (&lt;br&gt;
    &amp;lt;div className={styles.main}&amp;gt;&lt;br&gt;
      &amp;lt;h2 className={styles.noGapBottom}&amp;gt;Connect Your Wallet&amp;lt;/h2&amp;gt;&lt;br&gt;
      &amp;lt;p&amp;gt;Connect your wallet to check eligibility.&amp;lt;/p&amp;gt;&lt;br&gt;
      &amp;lt;button&lt;br&gt;
        onClick={connectWithMetamask}&lt;br&gt;
        className={&lt;/code&gt;${styles.mainButton} ${styles.spacerTop}&lt;code&gt;}&lt;br&gt;
      &amp;gt;&lt;br&gt;
        Connect Wallet&lt;br&gt;
      &amp;lt;/button&amp;gt;&lt;br&gt;
    &amp;lt;/div&amp;gt;&lt;br&gt;
  );&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State 3 - Connect Discord&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;jsx&lt;br&gt;
// 3. Connect with Discord (OAuth)&lt;br&gt;
if (!session) {&lt;br&gt;
  return (&lt;br&gt;
    &amp;lt;div className={&lt;/code&gt;${styles.main}`}&amp;gt;&lt;br&gt;
      &lt;/p&gt;
&lt;h2&gt;Sign In with Discord&lt;/h2&gt;
&lt;br&gt;
      &lt;p&gt;Sign In with Discord to check your eligibility for the NFT!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;button
    onClick={() =&amp;gt; signIn("discord")}
    className={`${styles.mainButton} ${styles.spacerTop}`}
  &amp;gt;
    Connect Discord
  &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;);&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// default return nothing&lt;br&gt;
return null;&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Back on our home page, let's change the logic to show the user the &lt;code&gt;SignIn&lt;/code&gt; component when we can't find both an &lt;code&gt;address&lt;/code&gt; and &lt;code&gt;session&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`jsx&lt;br&gt;
// index.tsx&lt;br&gt;
import { useAddress, useSDK } from "@thirdweb-dev/react";&lt;br&gt;
import { useSession } from "next-auth/react";&lt;br&gt;
import SignIn from "../components/SignIn";&lt;br&gt;
import type { NextPage } from "next";&lt;br&gt;
import styles from "../styles/Home.module.css";&lt;/p&gt;

&lt;p&gt;const Home: NextPage = () =&amp;gt; {&lt;br&gt;
  const address = useAddress();&lt;br&gt;
  const { data: session } = useSession();&lt;br&gt;
  const sdk = useSDK();&lt;/p&gt;

&lt;p&gt;return (&lt;br&gt;
    &lt;/p&gt;
&lt;br&gt;
      &lt;br&gt;
        
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    {address &amp;amp;&amp;amp; session &amp;amp;&amp;amp; (
      &amp;lt;div className={styles.collectionContainer}&amp;gt;
        &amp;lt;button className={styles.mainButton}&amp;gt;Give me the role!&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    )}
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;);&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;export default Home;&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We now have a page that prompts the user to connect their wallet, and then Sign In With Discord:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7H8dstIH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658444991246/-4ul2aaax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7H8dstIH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658444991246/-4ul2aaax.png" alt="image.png" width="714" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you click the &lt;code&gt;Connect Discord&lt;/code&gt; button, NextAuth handles the OAuth flow for us:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0VPTp3Ah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658445042195/hunL0jqm8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0VPTp3Ah--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658445042195/hunL0jqm8.png" alt="image.png" width="637" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the user has connected both their wallet and Discord, we show them a button that says &lt;code&gt;Give me the role!&lt;/code&gt;, we'll add some functionality to this button next!&lt;/p&gt;

&lt;h2&gt;
  
  
  Granting Discord Roles
&lt;/h2&gt;

&lt;p&gt;To grant a role to the connected user, we are going to use the Discord API on behalf of the bot that we created. Specifically, we'll be hitting the &lt;code&gt;Add Guild Member Role&lt;/code&gt; API endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o0PQYfHH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658455468735/Pe6SJyvls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o0PQYfHH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658455468735/Pe6SJyvls.png" alt="image.png" width="852" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make requests from our bot, we'll need a token to act on its behalf. To generate a token, head to the &lt;code&gt;Bot&lt;/code&gt; tab from your Discord Developer portal, and click &lt;code&gt;Reset Token&lt;/code&gt; on your bot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gLm60Ymz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658455558002/Y1ZUud12k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gLm60Ymz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658455558002/Y1ZUud12k.png" alt="image.png" width="880" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then need to store this inside our environment variables as well securely:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;text&lt;br&gt;
BOT_TOKEN=xxxx&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Next, let's make another API route in our &lt;code&gt;api&lt;/code&gt; folder called &lt;code&gt;grant-role.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Within this API route, we're going to grant a user the discord role &lt;em&gt;if&lt;/em&gt; they own an NFT from our collection. This involves a few steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Authenticate the login payload of the user (ensure the user owns the wallet)&lt;/li&gt;
&lt;li&gt;Check that wallet's NFT balance&lt;/li&gt;
&lt;li&gt;Make a request to the Discord API to grant a role&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Firstly, let's set up the barebones of our API route:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
import { ThirdwebSDK } from "@thirdweb-dev/sdk";&lt;br&gt;
import type { NextApiRequest, NextApiResponse } from "next";&lt;br&gt;
import { getSession } from "next-auth/react";&lt;/p&gt;

&lt;p&gt;export default async function grantRole(&lt;br&gt;
  req: NextApiRequest,&lt;br&gt;
  res: NextApiResponse,&lt;br&gt;
) {&lt;br&gt;
  // Get the login payload out of the request&lt;br&gt;
  const { loginPayload } = JSON.parse(req.body);&lt;/p&gt;

&lt;p&gt;// Get the NextAuth session so we can use the user ID as part of the discord API request&lt;br&gt;
  const session = await getSession({ req });&lt;/p&gt;

&lt;p&gt;if (!session) {&lt;br&gt;
    res.status(401).json({ error: "Not logged in" });&lt;br&gt;
    return;&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Authenticate the login payload
&lt;/h3&gt;

&lt;p&gt;On the client, we're going to ask the user to sign in using the &lt;a href="https://thirdweb.com/authentication?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=discord-role-granter"&gt;Authentication SDK&lt;/a&gt;. This requires the user to sign a message which generates a login payload; we'll be sending this in the body of the request.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`jsx&lt;br&gt;
// Authenticate login payload&lt;br&gt;
const sdk = new ThirdwebSDK("mumbai");&lt;br&gt;
const domain = "thirdweb.com"; // This should be the domain name of your own website&lt;br&gt;
// Verify the login payload is real and valid&lt;br&gt;
const verifiedWalletAddress = sdk.auth.verify(domain, loginPayload);&lt;/p&gt;

&lt;p&gt;// If the login payload is not valid, return an error&lt;br&gt;
if (!verifiedWalletAddress) {&lt;br&gt;
  res.status(401).json({ error: "Invalid login payload" });&lt;br&gt;
  return;&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This step ensures that the user owns the wallet that we are going to check. It prevents users from sending a wallet address that is not theirs to this API endpoint, and falsely granting them the role.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the wallet's NFT balance
&lt;/h3&gt;

&lt;p&gt;We use the SDK to view the balance of the wallet address for token ID &lt;code&gt;0&lt;/code&gt; of our ERC-1155 NFT collection.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
// Check if this user owns an NFT&lt;br&gt;
const editionDrop = sdk.getEditionDrop(&lt;br&gt;
  "0x1fCbA150F05Bbe1C9D21d3ab08E35D682a4c41bF",&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;// Get addresses' balance of token ID 0&lt;br&gt;
const balance = await editionDrop.balanceOf(verifiedWalletAddress, 0);&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Granting Users the role
&lt;/h3&gt;

&lt;p&gt;Here, we make the request to the discord API to grant the user a role by using our bot token as the authorization header.&lt;/p&gt;

&lt;p&gt;In order to do this, you'll need to create a role in your server, and copy both your &lt;code&gt;server&lt;/code&gt; and &lt;code&gt;role&lt;/code&gt; ID into the variables. You can learn how to do that from &lt;a href="https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ts&lt;br&gt;
if (balance.toNumber() &amp;gt; 0) {&lt;br&gt;
  // If the user is verified and has an NFT, return the content&lt;/p&gt;

&lt;p&gt;// Make a request to the Discord API to get the servers this user is a part of&lt;br&gt;
  const discordServerId = "999533680663998485";&lt;br&gt;
  const { userId } = session;&lt;br&gt;
  const roleId = "999851736028172298";&lt;br&gt;
  const response = await fetch(&lt;br&gt;
    // Discord Developer Docs for this API Request: &lt;a href="https://discord.com/developers/docs/resources/guild#add-guild-member-role"&gt;https://discord.com/developers/docs/resources/guild#add-guild-member-role&lt;/a&gt;&lt;br&gt;
    &lt;code&gt;https://discordapp.com/api/guilds/${discordServerId}/members/${userId}/roles/${roleId}&lt;/code&gt;,&lt;br&gt;
    {&lt;br&gt;
      headers: {&lt;br&gt;
        // Use the bot token to grant the role&lt;br&gt;
        Authorization: &lt;code&gt;Bot ${process.env.BOT_TOKEN}&lt;/code&gt;,&lt;br&gt;
      },&lt;br&gt;
      method: "PUT",&lt;br&gt;
    },&lt;br&gt;
  );&lt;/p&gt;

&lt;p&gt;// If the role was granted, return the content&lt;br&gt;
  if (response.ok) {&lt;br&gt;
    res.status(200).json({ message: "Role granted" });&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;// Something went wrong granting the role, but they do have an NFT&lt;br&gt;
  else {&lt;br&gt;
    res&lt;br&gt;
      .status(500)&lt;br&gt;
      .json({ error: "Error granting role, are you in the server?" });&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
// If the user is verified but doesn't have an NFT, return an error&lt;br&gt;
else {&lt;br&gt;
  res.status(401).json({ error: "User does not have an NFT" });&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it for our API route, now we need to call this from our client!&lt;/p&gt;

&lt;p&gt;Back on the &lt;code&gt;index.tsx&lt;/code&gt; page, let's create a function called &lt;code&gt;requestGrantRole&lt;/code&gt; &lt;em&gt;inside&lt;/em&gt; the component and make a fetch request to this API endpoint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`tsx&lt;br&gt;
const sdk = useSDK();&lt;/p&gt;

&lt;p&gt;async function requestGrantRole() {&lt;br&gt;
  // First, login and sign a message&lt;br&gt;
  const domain = "thirdweb.com"; // This should be the domain name of your own website&lt;br&gt;
  const loginPayload = await sdk?.auth.login(domain);&lt;/p&gt;

&lt;p&gt;// Then make a request to our API endpoint.&lt;br&gt;
  try {&lt;br&gt;
    const response = await fetch("/api/grant-role", {&lt;br&gt;
      method: "POST",&lt;br&gt;
      body: JSON.stringify({&lt;br&gt;
        loginPayload,&lt;br&gt;
      }),&lt;br&gt;
    });&lt;br&gt;
    const data = await response.json();&lt;br&gt;
    console.log(data);&lt;br&gt;
  } catch (e) {&lt;br&gt;
    console.error(e);&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And attach this function to our button:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;tsx&lt;br&gt;
&amp;lt;button className={styles.mainButton} onClick={requestGrantRole}&amp;gt;&lt;br&gt;
  Give me the role!&lt;br&gt;
&amp;lt;/button&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it! We're ready to test it out!&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;Connect our wallet, authenticate with Discord, and sign in with Ethereum:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C55R8psK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658461183495/yD-KJYnN6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C55R8psK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658461183495/yD-KJYnN6.png" alt="image.png" width="880" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;grant-role&lt;/code&gt; API endpoint runs, granting the connected Discord user the role if they have an NFT from the collection:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HGauSRde--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658461223440/Un7WxowZ4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HGauSRde--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658461223440/Un7WxowZ4.png" alt="image.png" width="738" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have the role in our server!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M11EVLqE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658461232901/LcwdvJccS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M11EVLqE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1658461232901/LcwdvJccS.png" alt="image.png" width="367" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Going to production
&lt;/h2&gt;

&lt;p&gt;In a production environment, you need to have an environment variable called &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt; for the Discord Oauth to work.&lt;/p&gt;

&lt;p&gt;You can learn more about it here:&lt;br&gt;
&lt;a href="https://next-auth.js.org/configuration/options"&gt;https://next-auth.js.org/configuration/options&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can quickly create a good value on the command line via this &lt;code&gt;openssl&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
openssl rand -base64 32&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And add it as an environment variable in your &lt;code&gt;.env.local&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
NEXTAUTH_SECRET=&amp;lt;your-value-here&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've made our very own Discord role granting application using thirdweb, the Discord API, and NextAuth!&lt;/p&gt;

&lt;p&gt;You can check out the full code for this project on our GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/thirdweb-example/discord-role-granter"&gt;https://github.com/thirdweb-example/discord-role-granter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For any questions, suggestions, join our discord at &lt;a href="https://discord.gg/thirdweb"&gt;https://discord.gg/cd thirdweb&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>discord</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Create an ERC-721A NFT Collection using thirdweb</title>
      <dc:creator>Jarrod Watts</dc:creator>
      <pubDate>Wed, 20 Jul 2022 04:15:00 +0000</pubDate>
      <link>https://dev.to/thirdweb/create-an-erc-721a-nft-collection-using-thirdweb-4ok0</link>
      <guid>https://dev.to/thirdweb/create-an-erc-721a-nft-collection-using-thirdweb-4ok0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this guide, we'll use the &lt;a href="https://portal.thirdweb.com/pre-built-contracts/signature-drop?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;Signature Drop&lt;/a&gt;&lt;br&gt;
contract to create a collection of NFTs that follow the &lt;a href="https://www.erc721a.org/"&gt;ERC-721A&lt;/a&gt; standard!&lt;/p&gt;

&lt;p&gt;Compared to the regular &lt;a href="https://eips.ethereum.org/EIPS/eip-721"&gt;ERC-721&lt;/a&gt; standard, the ERC-721A implementation&lt;br&gt;
optimizes the gas fees of the contract to enable cheaper gas fees for users when they are minting multiple tokens in one transaction.&lt;/p&gt;

&lt;p&gt;The signature drop contract also enables &lt;a href="https://portal.thirdweb.com/advanced-features/on-demand-minting?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;Signature-based Minting&lt;/a&gt;&lt;br&gt;
which allows us, (the admin wallet), to approve users' mint requests on-demand if they meet specific criteria, such as being a holder of another token!&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Here's what we'll be doing in this guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploying the ERC-721A Signature Drop contract.&lt;/li&gt;
&lt;li&gt;Lazy Minting our NFTs by batch uploading metadata files.&lt;/li&gt;
&lt;li&gt;Creating a web application where users can claim NFTs.&lt;/li&gt;
&lt;li&gt;Adding a special "Discount Claim" button where users who hold an NFT from another collection can claim an NFT for a discount!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the demo here: &lt;a href="https://signature-drop.thirdweb-example.com/"&gt;https://signature-drop.thirdweb-example.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can view the full source code below:&lt;/p&gt;

&lt;p&gt;%[&lt;a href="https://github.com/thirdweb-example/signature-drop"&gt;https://github.com/thirdweb-example/signature-drop&lt;/a&gt;]&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating the Signature Drop Contract
&lt;/h2&gt;

&lt;p&gt;To deploy one of thirdweb's &lt;a href="https://portal.thirdweb.com/smart-contracts/pre-built-contracts-overview?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;Pre-built contracts&lt;/a&gt;, head to the &lt;a href="https://thirdweb.com/dashboard?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;dashboard&lt;/a&gt; and connect your wallet.&lt;/p&gt;

&lt;p&gt;If you don't already have a wallet, learn how to get set up with MetaMask in our "&lt;a href="https://portal.thirdweb.com/guides/create-a-metamask-wallet?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;Guide: Create a MetaMask Wallet&lt;/a&gt;"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bO-okhzh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760009059/qfskZYlOE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bO-okhzh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760009059/qfskZYlOE.png" alt="image.png" width="782" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, click the &lt;code&gt;Deploy New Contract&lt;/code&gt; button -&amp;gt; &lt;code&gt;Pre-Built Contracts&lt;/code&gt; -&amp;gt; &lt;code&gt;Release a drop&lt;/code&gt; -&amp;gt; &lt;code&gt;Signature Drop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4eA8j3kc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760084261/ANCsZaEaV.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4eA8j3kc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760084261/ANCsZaEaV.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is where you can upload the metadata for the contract itself. This is the metadata that will show up on pages such as the OpenSea Collection page.&lt;/p&gt;

&lt;p&gt;Give your contract a &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;symbol&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, and configure royalty fees as you like! Here's how mine looks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--crXeqWzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760279907/Mj-8EOO8M.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--crXeqWzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760279907/Mj-8EOO8M.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll be deploying to the &lt;a href="https://portal.thirdweb.com/guides/which-network-should-you-use#arbitrum?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;Arbitrum Testnet&lt;/a&gt; as it&lt;br&gt;
offers super-fast transaction approval times (which are great for testing purposes)!&lt;/p&gt;

&lt;p&gt;When you're ready, click &lt;code&gt;Deploy Now&lt;/code&gt;, and approve the &lt;code&gt;Deploy Proxy&lt;/code&gt; transaction in your wallet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BVm-2b22--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760472247/2eSd8OwnC.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BVm-2b22--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760472247/2eSd8OwnC.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You just deployed your very own ERC-721A NFT Collection!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WIh9OW39--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760613270/9Jvj38jt5.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WIh9OW39--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760613270/9Jvj38jt5.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Batch Lazy-Minting NFTs
&lt;/h2&gt;

&lt;p&gt;In the Signature Drop contract, we &lt;strong&gt;lazy-mint&lt;/strong&gt; all of our NFTs by uploading the metadata using the Batch Upload feature.&lt;/p&gt;

&lt;p&gt;Click the &lt;code&gt;Batch Upload&lt;/code&gt; button to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4rZCn6wO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657761823467/mUK8ouWOn.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4rZCn6wO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657761823467/mUK8ouWOn.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, you'll be taken to a page where you can drag and drop your metadata and assets to upload them. You can use the example CSV and JSON files from the links on this page, or below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.thirdweb.com/example.csv"&gt;CSV Example File&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thirdweb.com/example.json"&gt;JSON Example FIle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a preview of mine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zKf9GLaw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657761972229/1JWKm4fYn.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zKf9GLaw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657761972229/1JWKm4fYn.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've prepared the files, drag them all into the upload box on the dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yt9wSg2j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657762030141/5eCE9x7dj.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yt9wSg2j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657762030141/5eCE9x7dj.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will allow you to preview your NFTs before lazy minting them.&lt;br&gt;
Ensure the properties and metadata appear how you expect, as you won't be able to modify them after they have been uploaded.&lt;/p&gt;

&lt;p&gt;Once you're happy, click &lt;code&gt;Next&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LnIwzMDW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657762109425/IEV_q4REY.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LnIwzMDW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657762109425/IEV_q4REY.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can then choose if you want to add a &lt;a href="https://portal.thirdweb.com/advanced-features/delayed-reveal"&gt;delayed reveal&lt;/a&gt; for your NFTs. For this guide, we'll leave it as &lt;code&gt;Reveal upon mint&lt;/code&gt; and click &lt;strong&gt;Upload NFTs&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aP3UOnwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657762191886/c81S6MIgM.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aP3UOnwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657762191886/c81S6MIgM.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuring A Claim Phase
&lt;/h2&gt;

&lt;p&gt;Claim phases are conditions that outline &lt;strong&gt;how&lt;/strong&gt;, &lt;strong&gt;when&lt;/strong&gt;, and &lt;strong&gt;who&lt;/strong&gt; can claim NFTs from your collection; such as an allowlist, release date, or &lt;a href="https://portal.thirdweb.com/advanced-features/delayed-reveal"&gt;delayed reveal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To add a claim phase, click the &lt;code&gt;Claim Phases&lt;/code&gt; tab and then the &lt;code&gt;Add Claim Phase&lt;/code&gt; button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0XScYFw8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760834325/UXHSvXkPm.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0XScYFw8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657760834325/UXHSvXkPm.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's where we can configure who can claim from this drop. I'm going to set the :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;How much do you want to charge to claim the NFTs?&lt;/code&gt; to be &lt;code&gt;0.01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;How many NFTs can be claimed per transaction?&lt;/code&gt; to be &lt;code&gt;1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;How many seconds do wallets have to wait in-between claiming?&lt;/code&gt; to be &lt;code&gt;Unlimited&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UnnNuc5O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657761119847/2ghOIC2ug.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UnnNuc5O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657761119847/2ghOIC2ug.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you're happy, click &lt;code&gt;Save Claim Phases&lt;/code&gt;!&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up the project
&lt;/h2&gt;

&lt;p&gt;We'll be using the &lt;a href="https://portal.thirdweb.com/thirdweb-deploy/thirdweb-cli"&gt;thirdweb CLI&lt;/a&gt; to initialize our application using Next.js and TypeScript.&lt;/p&gt;

&lt;p&gt;To create a new project using the CLI, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx thirdweb create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give your application a name and the project will be initialized for us! Let's open the project up in a text editor now, I'll be using VS Code.&lt;/p&gt;

&lt;p&gt;I'll be using the styles available in the &lt;a href="https://github.com/thirdweb-example/signature-drop/tree/main/styles"&gt;source code&lt;/a&gt;,&lt;br&gt;
feel free to copy these files into your project if you want to use the same styles.&lt;/p&gt;

&lt;p&gt;If we take a look at our &lt;code&gt;pages/app.tsx&lt;/code&gt; file, we have a configured &lt;code&gt;activeChainId&lt;/code&gt; as &lt;code&gt;Mainnet&lt;/code&gt; which is Ethereum Mainnet.&lt;/p&gt;

&lt;p&gt;Since we deployed our contract on the Arbitrum Test network let's change it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is the chainId your dApp will work on.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeChainId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ChainId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ArbitrumTestnet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Connecting to our contract
&lt;/h2&gt;

&lt;p&gt;Now we're ready to interact with our signature drop smart contract from within our code, to do that, we'll use the &lt;a href="https://portal.thirdweb.com/react/react.usesignaturedrop?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;useSignatureDrop&lt;/a&gt; hook from the React SDK.&lt;/p&gt;

&lt;p&gt;On the &lt;code&gt;index.tsx&lt;/code&gt; page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signatureDrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useSignatureDrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xBdEa6C9B18843cEA8262ead23767737512e452bC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get your contract address from the dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Xsg8MkF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657763367349/HUYsenavI.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Xsg8MkF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657763367349/HUYsenavI.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Claiming NFTs
&lt;/h2&gt;

&lt;p&gt;Since the wallet connection logic is already configured inside our application thanks to the thirdweb CLI, here's how our page looks now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAddress&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connectWithMetamask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMetamask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;disconnectWallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useDisconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signatureDrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useSignatureDrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xBdEa6C9B18843cEA8262ead23767737512e452bC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;disconnectWallet&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Disconnect Wallet&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Your address: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;connectWithMetamask&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Connect with Metamask&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The React SDK automatically uses the signer of the connected wallet when a user wants to perform an action on our smart contract such as &lt;code&gt;claim&lt;/code&gt; an NFT.&lt;/p&gt;

&lt;p&gt;Let's write the logic for that now. Firstly, the &lt;code&gt;claim&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signatureDrop&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And attach this function to a button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;claim&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Claim&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we preview our application by running &lt;code&gt;yarn dev&lt;/code&gt; from the terminal and visit &lt;a href="http://localhost:3000/"&gt;http://localhost:3000/&lt;/a&gt; we can see our claim button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ja3JagcS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657763880415/92oE6VCnc.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ja3JagcS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657763880415/92oE6VCnc.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click the &lt;code&gt;Claim&lt;/code&gt; button and approve the transaction, you can successfully mint the first NFT from the drop.&lt;/p&gt;

&lt;p&gt;Now in the dashboard, we have a &lt;strong&gt;Claimed Supply&lt;/strong&gt; of &lt;code&gt;1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That is the very basics of our minting page setup!&lt;/p&gt;

&lt;h2&gt;
  
  
  Claiming With Signature
&lt;/h2&gt;

&lt;p&gt;The signature drop contract also allows users to claim using a signature generated by an admin wallet. This is a three-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User requests a mint signature.&lt;/li&gt;
&lt;li&gt;Admin wallet (on the server) approves (or denies) the claim request of the user based on any criteria the admin chooses and sends back a mint signature when they approve.&lt;/li&gt;
&lt;li&gt;User uses the mint signature to claim NFTs from the signature drop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1UQP5omq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657771599960/zD2KaWxl1.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1UQP5omq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657771599960/zD2KaWxl1.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This enables all kinds of possibilities for us, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restrictions of who can claim based on off-chain data&lt;/li&gt;
&lt;li&gt;Discounts for users who meet some criteria (such as owning another NFT, or being part of a Discord server)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're going to check if the wallet owns one of our thirdweb &lt;a href="https://opensea.io/collection/thirdweb-community"&gt;Early Access Card NFTs&lt;/a&gt; and offer them a free mint if they do!&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the API Route
&lt;/h3&gt;

&lt;p&gt;Let's go back to our code, and under the &lt;code&gt;pages&lt;/code&gt; folder, create an &lt;code&gt;api&lt;/code&gt; folder. Within it, create a file called &lt;code&gt;generate-mint-signature.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's firstly create the basic structure of our API route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThirdwebSDK&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@thirdweb-dev/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;generateMintSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// De-construct body from request&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Next code snippet here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we call this API endpoint, we'll be passing an &lt;code&gt;address&lt;/code&gt; argument into the request body. In this API route, we de-structure that value so that we can use it when we generate the mint signature.&lt;/p&gt;

&lt;p&gt;Since we're going to provide the NFTs for free to wallets that own one of thirdweb's &lt;a href="https://opensea.io/collection/thirdweb-community"&gt;Early Access NFTs&lt;/a&gt;, let's check that now.&lt;/p&gt;

&lt;p&gt;To do that, we instantiate the thirdweb SDK with a read-only connection on the Polygon network and get the &lt;code&gt;thirdweb-community&lt;/code&gt; NFT Collection using its contract address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get the Early Access NFT Edition Drop contract&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;polygonSDK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ThirdwebSDK&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;polygon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;earlyAccessNfts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;polygonSDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getEditionDrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xa9e893cc12026a2f6bd826fdb295eac9c18a7e88&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// this is the thirdweb-community NFT Collection address.&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can use the &lt;code&gt;balanceOf&lt;/code&gt; function to check if the wallet that made this API request has any NFTs from that collection. Since it's an ERC-1155 collection, we check to see if they have a &lt;code&gt;balanceOf&lt;/code&gt; &lt;strong&gt;any&lt;/strong&gt; of the tokens in that collection in a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check to see if the wallet address has an early access NFT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;numTokensInCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;earlyAccessNfts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTotalCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userHasToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Check each token in the Edition Drop&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;numTokensInCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// See if they have the token&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;earlyAccessNfts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;userHasToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we have a &lt;code&gt;userHasToken&lt;/code&gt; variable that is true &lt;em&gt;if&lt;/em&gt; the user had a balance greater than zero for any token in that ERC-1155 NFT Collection.&lt;/p&gt;

&lt;p&gt;Now we can use that variable to determine whether or not we generate a mint signature for this user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Now use the SDK on Goerli to get the signature drop&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arbitrumSDK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ThirdwebSDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromPrivateKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arbitrum-testnet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signatureDrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arbitrumSDK&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSignatureDrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-contract-address-here&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// Your contract address here&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// If the user has an early access NFT, generate a mint signature&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userHasToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mintSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signatureDrop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Can only be minted by the address we checked earlier&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Free!&lt;/span&gt;
      &lt;span class="na"&gt;mintStartTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// now&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mintSignature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User does not have an early access NFT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above snippet, we are using our private key to instantiate the SDK on behalf of our admin wallet.&lt;/p&gt;

&lt;p&gt;Learn how to &lt;a href="https://portal.thirdweb.com/guides/create-a-metamask-wallet#export-your-private-key?utm_source=devto&amp;amp;utm_medium=crosspost&amp;amp;utm_campaign=signature-drop"&gt;export your private key&lt;/a&gt; from your wallet.&lt;/p&gt;

&lt;p&gt;To do this, create a file called &lt;code&gt;.env&lt;/code&gt; at the root of your project, and add the following to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRIVATE_KEY=your-private-key-here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ensure you store and access your private key securely.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the API Route on the Front-end
&lt;/h3&gt;

&lt;p&gt;On our home page, we need a way to call our API route, let's write that function now.&lt;/p&gt;

&lt;p&gt;Here, we check to see if the mint signature came back from the API request. If it didn't, we display an error message. If it did, we call the&lt;br&gt;
&lt;code&gt;signature.mint&lt;/code&gt; with the mint signature to claim the NFT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;claimWithSignature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signedPayloadReq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/generate-mint-signature`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signedPayloadReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Looks like you don't own an early access NFT :( You don't qualify for the free mint.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signedPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signedPayloadReq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;SignedPayload721WithQuantitySignature&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signedPayload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;signatureDrop&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signedPayload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Succesfully minted NFT!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all we need to do is add a new button that calls this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;claimWithSignature&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Claim&lt;/span&gt; &lt;span class="nx"&gt;Free&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's test it out:&lt;/p&gt;

&lt;p&gt;On a wallet that &lt;strong&gt;doesn't&lt;/strong&gt; own one of these Early Access NFTs, the mint signature is not granted to them, meaning they are unable to mint for free using the signature minting method:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p4kKldij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657866182974/CUcMmHkNJ.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p4kKldij--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657866182974/CUcMmHkNJ.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On a wallet that &lt;strong&gt;does&lt;/strong&gt; own the Early Access NFTs, they are allowed to mint an NFT for free rather than paying with the regular claim method!&lt;/p&gt;

&lt;p&gt;Once the transaction is confirmed, we should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F_mi05lx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657866716782/U1Tm9cOL9.png%2520align%3D%2522center%2522" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F_mi05lx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1657866716782/U1Tm9cOL9.png%2520align%3D%2522center%2522" alt="image.png" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it! You have successfully created your very own ERC-721A NFT collection with signature minting, and built an app to claim your NFTs on top of it!&lt;/p&gt;

&lt;p&gt;To learn more about what you can do with the signature drop contract, check out the &lt;a href="https://portal.thirdweb.com/pre-built-contracts/signature-drop"&gt;Portal documentation&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>web3</category>
      <category>tutorial</category>
      <category>typescript</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
