龙空技术网

Web3教程:仅仅一个周末,只用JS就可以构建出你自己的DAO(3)

李留白 1046

前言:

此时看官们对“js教程3”大概比较着重,同学们都需要了解一些“js教程3”的相关文章。那么小编也在网上收集了一些对于“js教程3””的相关文章,希望咱们能喜欢,同学们快快来了解一下吧!

目录

开始

• 让我们构建一些很酷的东西。• 什么是 DAO?

为 DAO 设置客户端应用程序

• 获取客户端代码 + 获取设置。• 将“连接到钱包”添加到您的 DAO 仪表板。

创建会员 NFT

• 部署你的 NFT 包。• 部署 NFT 元数据。• 让用户铸造你的 NFT。

创建代币+治理

• 部署 ERC-20 合约。• 在 DAO Dashboard 上展示代币持有者。• ‍⚖️ 建库+治理。• ✅ 让用户对提案进行投票。

收尾工作

• 删除您的管理员权限并处理基本错误。• 完成并庆祝。 部署你的 NFT 包。 开始使用thirdweb

真棒!我们现在可以连接到用户的钱包,这意味着我们现在可以检查他们是否在我们的 DAO 中!为了加入我们的 DAO,用户需要会员资格 NFT。如果他们没有会员 NFT,我们会提示他们创建会员 NFT 并加入我们的 DAO!

但是,有一个问题。为了让我们铸造 NFT,我们需要编写 + 部署我们自己的 NFT 智能合约。这实际上是thirdWeb 发挥作用的地方。

thirdWeb 为我们提供了一套工具,无需编写任何 Solidity 即可创建我们所有的智能合约。

我们不写 Solidity。我们需要做的就是只使用 JavaScript 编写一个脚本来创建和部署我们的合约。thirdweb 将使用他们在这里[1]创建的一组安全、标准的合约。很酷的部分是在您创建合约之后,您拥有它们并且它们与您的钱包相关联。

部署合约后,您可以使用它们的客户端 SDK 从前端轻松地与这些合约进行交互。

我无法形容与编写你自己的Solidity代码相比,用thirdweb创建一个智能合约是多么容易,它感觉就像与一个普通的后台库互动。让我们开始吧。

thirdweb 仪表板[2]允许我们在不编写任何代码的情况下创建合约,但在本教程中,我们将使用 JavaScript 创建它们。

重要的!thirdweb 没有数据库,您的所有数据都存储在链上。

创建一个运行thirdweb脚本的地方

现在我们需要实际编写一些脚本,让我们使用thirdweb 创建/部署我们的合约到Goerli。我们要做的第一件事是在项目的根目录中创建一个看起来像这样的.env文件。

PRIVATE_KEY=YOUR_PRIVATE_KEY_HEREWALLET_ADDRESS=YOUR_WALLET_ADDRESSQUICKNODE_API_URL=YOUR_QUICKNODE_API_URL

注意:在 Repli?你需要使用这个[3]。基本上,.env 文件在 Replit 上不起作用。您应该使用此方法逐一添加具有相同名称的变量。完成后,您需要通过停止/运行 repo 来启动 Replit,以便它获取新的 env 变量!

thirdweb 需要所有这些东西来代表您部署合同。他们没有存储任何内容,所有内容都保留在本地.env文件中。不要将您的.env文件提交到 Github。你的信息会被盗走。当心。

要从 Metamask 获取您的私钥,请查看此[4]内容。

要获取您的钱包地址,请查看此[5]内容。

快速节点

您在.env文件中需要的最后一件事是QUICKNODE_API_URL.

QuickNode 本质上帮助我们广播我们的合约创建交易,以便它可以尽快被测试网上的矿工拾取。一旦交易被挖掘出来,它就会作为合法交易被广播到区块链上。从那里,每个人都会更新他们的区块链副本。

因此,请在此处[6]使用 QuickNode 注册一个帐户 。

看看下面的视频,看看如何为测试网获得你的API密钥! 不要乱来,创建一个主网密钥,我们需要一个测试网密钥。 查看下面的视频,了解如何获取测试网的 API!

您现在应该在.env文件中包含这三个项目了!

初始化 SDK

前往scripts/1-initialize-sdk.js。

import { ThirdwebSDK } from "@thirdweb-dev/sdk";// Importing and configuring our .env file that we use to securely store our environment variablesimport dotenv from "dotenv";dotenv.config();// Some quick checks to make sure our .env is working.if (!process.env.PRIVATE_KEY || process.env.PRIVATE_KEY === "") {  console.log(" Private key not found.");}if (!process.env.QUICKNODE_API_URL || process.env.QUICKNODE_API_URL === "") {  console.log(" QuickNode API URL not found.");}if (!process.env.WALLET_ADDRESS || process.env.WALLET_ADDRESS === "") {  console.log(" Wallet Address not found.");}const sdk = ThirdwebSDK.fromPrivateKey(  // Your wallet private key. ALWAYS KEEP THIS PRIVATE, DO NOT SHARE IT WITH ANYONE, add it to your .env file and do not commit that file to github!  process.env.PRIVATE_KEY,  // RPC URL, we'll use our QuickNode API URL from our .env file.  process.env.QUICKNODE_API_URL)(async () => {  try {    const address = await sdk.getSigner().getAddress();    console.log(" SDK initialized by address:", address)  } catch (err) {    console.error("Failed to get apps from the sdk", err);    process.exit(1);  }})();// We are exporting the initialized thirdweb SDK so that we can use it in our other scriptsexport default sdk;

确保我们的 sdk 初始化正确!

在执行该功能之前,请确保您已安装 Node 16+,您可以使用以下命令检查您的版本:

node -v

注意:如果您在 Replit 上,您实际上可以从它提供的 shell 运行脚本:

如果你有旧版本的 Node,你可以在这里[7]更新它。(下载 LTS 版本)

注意:如果您在 Replit 上,您实际上可以通过从 shell 运行它来更新节点版本:

npm init -y && npm i --save-dev node@17 && npm config set prefix=$(pwd)/node_modules/node && export PATH=$(pwd)/node_modules/node/bin:$PATH

让我们执行它!转到您的终端并粘贴以下命令:

node scripts/1-initialize-sdk.js

这是我运行脚本时得到的结果。

buildspace-dao-starter % node scripts/1-initialize-sdk.js SDK initialized by address: 0xF11D6862e655b5F4e8f62E00471261D2f9c7E380

注意:您可能还会看到一些随机警告,例如ExperimentalWarning,请确保您的应用地址已打印出来!

史诗。如果你看到它打印出你的钱包地址,那么这意味着一切都已初始化!

创建一个 ERC-1155 集合

我们现在要做的是创建 + 部署 ERC-1155 合约到 Goerli。这基本上是我们创建 NFT 所需的基本模块。我们还没有在这里创建我们的 NFT。我们只是围绕集合本身设置元数据。这类似于集合的名称(例如 CryptoPunks)以及与集合相关联的图像,该图像在 OpenSea 上显示为标题。

注意:您可能知道 ERC-721,其中每个 NFT 都是唯一的,即使它们具有相同的图像、名称和属性。使用 ERC-1155,多人可以成为同一个 NFT 的持有者。在这种情况下,我们的“会员 NFT”对每个人都是一样的,所以我们不必每次都创建一个新的 NFT,而可以简单地将相同的 NFT 分配给我们的所有成员。这也更省gas!对于所有持有者的 NFT 相同的情况,这是一种非常常见的方法。

前往scripts/2-deploy-drop.js并添加以下代码:

import { AddressZero } from "@ethersproject/constants";import sdk from "./1-initialize-sdk.js";import { readFileSync } from "fs";(async () => {  try {    const editionDropAddress = await sdk.deployer.deployEditionDrop({      // The collection's name, ex. CryptoPunks      name: "NarutoDAO Membership",      // A description for the collection.      description: "A DAO for fans of Naruto.",      // The image that will be held on our NFT! The fun part :).      image: readFileSync("scripts/assets/naruto.png"),      // We need to pass in the address of the person who will be receiving the proceeds from sales of nfts in the contract.      // We're planning on not charging people for the drop, so we'll pass in the 0x0 address      // you can set this to your own wallet address if you want to charge for the drop.      primary_sale_recipient: AddressZero,    });    // this initialization returns the address of our contract    // we use this to initialize the contract on the thirdweb sdk    const editionDrop = await sdk.getContract(editionDropAddress, "edition-drop");    // with this, we can get the metadata of our contract    const metadata = await editionDrop.metadata.get();    console.log(      "✅ Successfully deployed editionDrop contract, address:",      editionDropAddress,    );    console.log("✅ editionDrop metadata:", metadata);  } catch (error) {    console.log("failed to deploy editionDrop contract", error);  }})();

一个非常简单的脚本!

我们给我们的集合一个name, descriptionand primary_sale_recipient, and image。我们正在从本地文件加载image,因此请务必将该图像包含在scripts/assets. 现在确保它是 PNG、JPG 或 GIF,并确保它是本地图像 - 如果您使用 Internet 链接,这将不起作用!

当我使用 node scripts/2-deploy-drop.js运行它时,我得到了。

buildspace-dao-starter % node scripts/2-deploy-drop.js SDK initialized by address: 0xF11D6862e655b5F4e8f62E00471261D2f9c7E380✅ Successfully deployed editionDrop contract, address: 0xE56fb4F83A9a99E40Af1C7eF08643e7bf1259A95✅ editionDrop metadata: {  name: 'NarutoDAO Membership',  description: 'A DAO for fans of Naruto.',  image: ';,  seller_fee_basis_points: 0,  fee_recipient: '0x0000000000000000000000000000000000000000',  merkle: {},  symbol: ''}

好吧,刚刚发生的事情真是太有意思了。发生了两件事:

一,我们刚刚向 Goerli部署了ERC-1155合约。[8]这是正确的!如果您前往并粘贴editionDrop合约地址,您会看到您刚刚部署了一个智能合约!最酷的部分是你拥有这个合约,它是从你的钱包中部署的。“发件人”地址将是您的公共地址。

注意:请保留您的editionDrop地址,我们稍后需要它!,如果您丢失了它,您可以随时从thirdweb 仪表板中检索找到[9]。

很有史诗感。。一个部署好的,只用javascript的定制合同。您可以在此处查看[10]thirdweb 使用的实际智能合约代码。

我们在这里做的另一件事是 thirdweb 自动上传并将我们收藏的图像固定到 IPFS。您会看到一个以打印出来的链接开头。如果您将其粘贴到浏览器中,您将看到您的 NFT 图像正在通过 CloudFlare 从 IPFS 检索!

你甚至可以直接使用 ipfs:// URI 来访问 IPFS(注意——这不能在 Chrome 上工作,因为你需要运行一个 IPFS 节点,但可以在 Brave 浏览器上为你做到这一点!)

注意:IPFS 基本上是一个去中心化的存储系统,在这里[11]阅读更多信息!

如果您以前在 Solidity 中开发过自定义智能合约,这有点令人兴奋。我们已经将合约部署到 Goerli + IPFS 上托管的数据。狂野。接下来,我们需要实际创建我们的 NFT!

部署 NFT 元数据 设置NFT数据

好的,现在我们要实际部署与我们的会员资格NFT相关的元数据。我们还没有做到这一点。到目前为止,我们所做的就是创建ERC-1155合约,并添加一些基本的元数据。我们还没有实际设置我们的会员制NFT,让我们来做这件事!

前往scripts/3-config-nft.js并添加进去:

import sdk from "./1-initialize-sdk.js";import { readFileSync } from "fs";(async () => {  try {    const editionDrop = await sdk.getContract("INSERT_EDITION_DROP_ADDRESS", "edition-drop");    await editionDrop.createBatch([      {        name: "Leaf Village Headband",        description: "This NFT will give you access to NarutoDAO!",        image: readFileSync("scripts/assets/headband.png"),      },    ]);    console.log("✅ Successfully created a new NFT in the drop!");  } catch (error) {    console.error("failed to create the new NFT", error);  }})();

很简单!

我们要做的第一件事是访问我们的editionDrop合约,它是一个ERC-1155。INSERT_EDITION_DROP_ADDRESS是上一步打印出来的地址。这就是Successfully deployed editionDrop contract, address后面打印出来的地址。 您还可以在您的thirdweb 仪表板上[12]找到它。您的 thirdweb 仪表板将显示您当前正在处理的合同,它还会显示地址,以便您轻松复制和粘贴。

然后,我们在 ERC-1155 上使用createBatch设置我们的实际NFT. 我们需要设置一些属性:

• name:我们 NFT 的名称。• description : 我们 NFT 的描述• image:我们 NFT 的图像。这是用户声称能够访问您的 DAO 的 NFT 的图像。

请记住,因为它是 ERC-1155,所以我们所有的成员都会铸造相同的 NFT。

请务必替换image: readFileSync("scripts/assets/headband.png")为您自己的图像。和以前一样,请确保它是本地图像,因为如果您使用互联网链接,这将不起作用。

我正在建造 NarutoDAO,所以,我的成员需要一个 Leaf Village Headband 才能加入 :

发挥你的创意,不要抄袭!

准备好后,运行:

node scripts/3-config-nft.js

这是我得到的:

 SDK initialized by address: 0xF11D6862e655b5F4e8f62E00471261D2f9c7E380✅ Successfully created a new NFT in the drop!
设置索赔条件

现在我们需要实际设置我们的“索赔条件”。可以铸造的最大 NFT 数量是多少?用户什么时候可以开始铸造 NFT?同样,这通常是您需要写入合约的自定义逻辑,但在这种情况下,thirdweb 使它变得容易。

前往scripts/4-set-claim-condition.js并添加:

import sdk from "./1-initialize-sdk.js";import { MaxUint256 } from "@ethersproject/constants";(async () => {  try {    const editionDrop = await sdk.getContract("INSERT_EDITION_DROP_ADDRESS", "edition-drop");    // We define our claim conditions, this is an array of objects because    // we can have multiple phases starting at different times if we want to    const claimConditions = [{      // When people are gonna be able to start claiming the NFTs (now)      startTime: new Date(),      // The maximum number of NFTs that can be claimed.      maxClaimable: 50_000,      // The price of our NFT (free)      price: 0,      // The amount of NFTs people can claim in one transaction.      maxClaimablePerWallet: 1,      // We set the wait between transactions to unlimited, which means      // people are only allowed to claim once.      waitInSeconds: MaxUint256,    }]    await editionDrop.claimConditions.set("0", claimConditions);    console.log("✅ Sucessfully set claim condition!");  } catch (error) {    console.error("Failed to set claim condition", error);  }})();

这里和以前一样,一定要替换INSERT_EDITION_DROP_ADDRESS为你的 ERC-1155 合约的地址。

startTime是允许用户开始铸造 NFT 的时间,在这种情况下,我们只需将该日期/时间设置为当前时间,这意味着铸造可以立即开始。

maxClaimable是可以铸造的最大成员 NFT 数。

maxClaimablePerWallet指定某人可以在单笔交易中索取多少代币,我们将其设置为1个,因为我们只希望用户一次铸造一个 NFT!在某些情况下,您可能希望一次向您的用户铸造多个 NFT(例如,当他们打开一个包含多个 NFT 的战利品箱时),但在这种情况下,我们只需要1个。

price设置我们 NFT 的价格,在我们的例子中,免费设置为 0。

waitInSeconds是交易之间的时间量,因为我们只希望人们认领一次,我们将其设置为区块链允许的最大数量。

最后,我们这样执行了editionDrop.claimConditions.set("0", claimConditions),这实际上将与我们在链上部署的合约进行交互并调整条件,非常酷!为什么我们要传入 0?好吧,基本上我们的会员 NFT 有一个tokenId为0,因为它是我们 ERC-1155 合约中的第一个代币。请记住——使用 ERC-1155,我们可以让多个人铸造同一个 NFT。在这种情况下,每个人都会铸造一个带有 id 为 0的 NFT。但是,我们也可以有一个不同的 NFT 以及 id为 1,也许我们将 NFT 提供给我们的 DAO 中优秀的成员!这完全取决于我们。

运行后node scripts/4-set-claim-condition.js,我得到:

 SDK initialized by address: 0xF11D6862e655b5F4e8f62E00471261D2f9c7E380✅ Successfully set claim condition!

砰!我们已经成功地与我们部署的智能合约进行了交互,并为我们的 NFT 提供了它必须遵循的某些规则!如果您复制粘贴打印在那里的bundle drop地址并在其中搜索,您将在那里看到我们与合同进行交互的证据!

让用户铸造你的 NFT

让我们前往App.jsx,我们现在要做的是

1. 如果我们检测到我们的用户拥有 NFT 会员资格,请向他们展示我们的“DAO 仪表板”屏幕,他们可以在其中对提案进行投票并查看 DAO 相关信息。2. 如果我们检测到用户没有我们的 NFT,我们会给他们一个按钮来铸造一个。

我们开始做吧!我们将首先开发case#1,我们需要检测用户是否拥有我们的 NFT。

检查用户是否拥有会员 NFT

前往App.jsx,将我们的导入更新为:

import { useAddress, ConnectWallet, useContract, useNFTBalance } from '@thirdweb-dev/react';import { useState, useEffect, useMemo } from 'react';

在console.log(" Address:", address);下面,我们将在下面添加:

  // Initialize our Edition Drop contract  const editionDropAddress = "INSERT_EDITION_DROP_ADDRESS"  const { contract: editionDrop } = useContract(editionDropAddress, "edition-drop");  // Hook to check if the user has our NFT  const { data: nftBalance } = useNFTBalance(editionDrop, address, "0")  const hasClaimedNFT = useMemo(() => {    return nftBalance && nftBalance.gt(0)  }, [nftBalance])  // ... include all your other code that was already there below.

我们首先初始化我们的editionDrop合约。

我们用useNFTBalance来检查当前连接的账户持有多少 NFT。这实际上将从我们部署的智能合约查询以获取数据。我们为什么要使用"0"?好吧,基本上是因为如果你记得"0"是我们的会员 NFT的tokenId。所以,在这里我们问我们的合约,“嘿,这个用户是否拥有一个 id为"0" 的代币?”。

现在,我们知道了一个用户没有NFT的情况 !让我们创建一个按钮让用户铸造一个。

✨ 建立一个“Mint NFT”按钮

我们开始吧!回到App.jsx。我在添加的行上添加了一些评论:

import { useAddress, ConnectWallet, Web3Button, useContract, useNFTBalance } from '@thirdweb-dev/react';import { useState, useEffect, useMemo } from 'react';const App = () => {  // Use the hooks thirdweb give us.  const address = useAddress();  console.log(" Address:", address);  // Initialize our Edition Drop contract  const { contract: editionDrop } = useContract("INSERT_EDITION_DROP_ADDRESS", "edition-drop");  // Hook to check if the user has our NFT  const { data: nftBalance } = useNFTBalance(editionDrop, address, "0")  const hasClaimedNFT = useMemo(() => {    return nftBalance && nftBalance.gt(0)  }, [nftBalance])  // This is the case where the user hasn't connected their wallet  // to your web app. Let them call connectWallet.  if (!address) {    return (      <div className="landing">        <h1>Welcome to NarutoDAO</h1>        <div className="btn-hero">          <ConnectWallet />        </div>      </div>    );  }  // Render mint nft screen.  return (    <div className="mint-nft">      <h1>Mint your free DAO Membership NFT</h1>      <div className="btn-hero">        <Web3Button           contractAddress={editionDropAddress}          action={contract => {            contract.erc1155.claim(0, 1)          }}          onSuccess={() => {            console.log(` Successfully Minted! Check it out on OpenSea: {editionDrop.getAddress()}/0`);          }}          onError={error => {            console.error("Failed to mint NFT", error);          }}        >          Mint your NFT (FREE)        </Web3Button>      </div>    </div>  );}export default App;

我们正在使用Web3Button创建一个按钮,该按钮将在我们的editionDrop合约上调用我们的claim函数。我们传入 用户的 address, 1是NFT 的铸造amount,以及0是NFT的铸币tokenId。

这将使我们的智能合约产生一个交易,并为用户铸造 NFT。我们还传入onSuccess和onError回调来处理成功和错误情况。

当你真正去铸造 NFT 时,Metamask 会弹出,以便你可以支付 gas。完成铸币后,您应该会在控制台中看到Successfully Minted!,以及 Testnet OpenSea 的链接。我们实际上可以在testnets.opensea.io[13]测试网上看到铸造的 NFT,这非常酷!当您前往您的链接时,您会看到如下内容:

很好!在这里你会看到我的 NFT 有“6 个所有者”。您还会看到它说“您拥有 1”,只要您铸造了一个,您就会看到它!

这是因为我实际上有几个朋友为我铸造了这个NFT来测试它。此外,因为它是 ERC-1155,所以每个人都是同一个 NFT 的所有者。这很酷,而且更省gas。铸造1个 ERC721 的成本为 96,073 gas。铸造 ERC1155 需要 51,935 个 gas。为什么?因为每个人都在共享相同的 NFT 数据。我们不必为每个用户复制新数据。

仅当用户拥有 NFT 时才显示 DAO Dashboard

好的,所以如果你还记得我们需要处理两种情况:

1. 如果我们检测到我们的用户拥有 NFT 会员资格,请向他们展示我们的“DAO 仪表板”屏幕,他们可以在其中对提案进行投票并查看 DAO 相关信息。2. 如果我们检测到用户没有我们的 NFT,我们会给他们一个按钮来铸造一个。

这很容易。渲染 mint nft 屏幕之前,我们需要在App.jsx添加以下内容。

if (!address) {  return (    <div className="landing">      <h1>Welcome to NarutoDAO</h1>      <div className="btn-hero">        <ConnectWallet />      </div>    </div>  );}// Add this little piece!if (hasClaimedNFT) {  return (    <div className="member-page">      <h1>DAO Member Page</h1>      <p>Congratulations on being a member</p>    </div>  );};

现在,当您刷新页面时,您会看到您位于 DAO 成员页面中。是的!!!如果您断开您的钱包与您的网络应用程序的连接,您将被带到“连接钱包”页面。

最后,如果你连接你的钱包并且没有NFT 会员资格,它会提示你铸造一个。我建议你测试这个案例:

1. 断开您的钱包与您的网络应用程序的连接2. 实际创建一个新帐户[14]

这将为您提供一个新的公共地址,以便您可以有一个新地址来接收 NFT。Metamask 让您拥有任意数量的帐户。

一定要测试所有三种情况!

1. 钱包未连接:1. 钱包已连接,但用户没有会员 NFT:1. 用户拥有会员资格 NFT,因此,向他们展示只有 DAO 成员才能看到的页面:

Github源码:

原文链接:

引用链接

[1] 这里:

[2] thirdweb 仪表板:

[3] 这个:

[4] 此:

[5] 此:

[6] 因此,请在此处:

[7] 在这里:

[8] ERC-1155合约。:

[9] thirdweb 仪表板中检索找到:

[10] 您可以在此处查看:

[11] 这里:

[12] thirdweb 仪表板上:

[13] testnets.opensea.io:

[14] 创建一个新帐户:

标签: #js教程3