Advertisement

【区块链 | Web3】Web3 全栈指南

阅读量:

原文https://betterprogramming.pub/everything-you-need-to-know-about-fullstack-web3-94c0f1b18019
译文来源:源自登链项目组
译制团队:由翻译团队负责
校对人员:由Tiny 熊负责
固定链接:文章固定链接为 learnblockchain.cn/article…

最近你可能已经开发了一个基于Solidity或Rust的区块链项目...然而即使有了良好的前端交互体验,这个区块链技术也可能难以被广泛采用。

在本文中,我们将深入解析如何在Web开发框架中实现与智能合约或其他链上应用的无缝集成。该方法将详细探讨如何通过HTML语言构建基础页面并结合JavaScript脚本实现动态交互机制

采用六种不同的方法实现 wallet 地址的连接,并将你的 Metamask、Phantom 或其他区块链钱包地址集成至前端界面。接下来我们将探索一些流行于 Next.js 与 React 生态中的相关软件包(

那么,让我们开始吧。

介绍

旨在提升Web3用户体验的友好程度方面

  1. 请详细说明如何通过这些钱包连接到用户界面?
  2. 请阐述在网站上调用智能合约进行交易的具体方法?
  3. 现有的最佳实践正在利用哪些工具?

改写说明

掌握当我们在与区块链交互或向其发送交易时,在浏览器中会发生的情况。学会识别不同类型的交易是如何被处理以及它们在系统中的位置。

探索连接到我们web3应用程序的六种最常用方法。这些方法涵盖了从简单的API调用来复杂的脚本执行过程的不同层面。

提供代码范例,并列举该领域内最大参与者所采用的主要工具。这样一来,我们也能采用这些工具以实现类似的功能和效果。

如果你想深入了解当前一些知名前端平台的表现形式,请访问一下AaveUniswap网站。

兴奋吗?我也是。我们开始吧。

如何将使用Metamask连接到智能合约

除了目前的之外,还可以是其他钱包类型.比如浏览器内的另一个钱包,包括PhantomWalletConnect等.

在区块链领域中,并非所有应用都局限于采用诸如Hardhat等技术方案进行构建;与此同时,在前端开发中,则主要依赖于经典的网页制作技术体系:HTML语言体系、JavaScript编程语言以及CSS样式表语言的基础上进行拓展与集成运用。此外,在开发过程中还会大量结合NextJS框架系统(基于NextJS生态)、React生态套件(基于React平台)以及Angular开发平台(基于Angular技术)进行功能扩展与交互设计。

因此,如果你熟悉传统的网络开发,你就会走在别人的前面!

在浏览器中使用Metamask

请按照以下步骤进行操作:首先,请安装并启用[Metamask]。随后,请访问 accompanying video以获取更多详细信息。完成安装后,在网页界面右键点击。接着点击"检查(inspect)":

右键点击屏幕,在弹出菜单中选择“检查(Inspect)”或“检查元素(Inspect Element)”。展开后将看到以下内容:

该系统使用代码来显示和渲染网站页面。
单击顶部栏中的 sources 按钮后,则会查看更多详细信息。
(注意:在某些情况下,请确保已找到 sources 后才能通过点击 >> 按钮来查看更多选项)

如果在浏览器中选择了配置选项并勾选了 Metamask,则左侧会出现带有 Metamask 标识的文件;如果你选择了配置选项并勾选了 Phantom,则会看到带有 Phantom 标识的文件。

这些插件运行一些有趣的功能,并嵌入到你的网站中,赋予网站与这些插件互动的机会。

每个浏览器都配备了一个名为window的对象。通过点击控制台中的console标签,您能够访问JavaScript控制台并执行命令。访问此工具的位置为JavaScript控制台。输入窗口名即可查看该对象的属性和方法:选择该对象作为目标将使您能够执行所需操作。

让我们继续输入window,看看我们得到什么。

通过浏览器界面观察到JavaScript中的window对象存在。由于我们安装了Metamask软件,在JavaScript默认环境中新增了一个位于Window对象上的Ethereum属性。尝试通过键盘按压快捷键打开此功能(如果你拥有Phantom钱包,则可以选择查看Solana地址)。

程序返回了一个对象。如果未安装Metamask插件,则会得到一个undefined值。不同浏览器的钱包可能会在window对象中添加不同的属性。通常情况下,在各个钱包的具体文档中都可以找到相关信息。有关该功能的详细说明,请参考Metamask官方指南对该属性进行了详细的描述。

注意:在以前的版本中,为window.web3,后来改为window.ethereum

这就是所谓的区块链提供者(provider),那么我们为什么需要这个呢?

区块链连接与提供者(Provider)

为了访问区块链上的数据或进行交易操作时,必须先建立与区块链网络的连接。在此过程中,一旦提交交易请求,系统会自动将该请求推送给主网并发起广播。此外,在完成交易后,在线服务系统会将生成的电子收据立即通过指定通道发送给客户账户。一旦客户账户收到该电子收据后,则表示该用户已成功完成此次支付操作并获得相应的资金结算。

你可能曾经在区块链应用程序中涉及过Alchemy、Infura或Moralis Speedy Nodes等工具的RPC URL接口。这些网络服务供应商都属于'节点即服务(node-as-a-service)'模式下的提供者群体,在此框架下它们会向区块链网络提供一个HTTP端点以便接收相关请求信息。加密货币钱包如Metamask同样遵循这一模式,在其内置功能中提供了与区块链节点直接通信的能力。事实上,在Metamask网络标签页面上(Metamask network 标签),你可以深入查看并了解当前所使用的RPC URL地址信息

这表明,在每次使用Metamask进行操作时(或都依赖于),都会通过这个RPC URL来进行API请求。

用HTML和JavaScript连接到加密货币钱包

我们将重点介绍如何在前端技术中实现这一目标,并深入探讨基于Next.js框架的实现方式。具体而言,在我的GitHub存储库https://github.com/PatrickAlphaC/html-js-ethers-connect中提供了完整的HTML与JavaScript连接钱包的示例代码包;此外,在另一个仓库https://github.com/PatrickAlphaC/full-stack-web3-metamask-connect中则包含了完整的代码集合以供参考。

改写说明

复制代码
 <!DOCTYPE html>

    
 <html>
    
   <head>
    
     <title>Javascript Test</title>
    
   </head><body>
    
     <button id="connectButton">Connect</button>
    
   </body>
    
 </html>

我们可以对按钮进行功能扩展,并对其实现过程进行详细规划:首先,在按钮页面上配置一个script标签;其次,在这个页面中编写一个JavaScript函数;最后,在该页面中获取并定位到该元素(如ethereum),如果定位到该元素(如ethereum)存在,则向其发送连接请求以建立通信渠道。

复制代码
 <!DOCTYPE html>

    
 <head>
    
     <title>Javascript Test</title>
    
 </head>
    
 <body>
    
     <button id="connectButton" onclick="connect()">Connect</button>
    
 </body>
    
 <script>
    
 async function connect() {
    
   if (typeof window.ethereum !== "undefined") {
    
     try {
    
       await window.ethereum.request({ method: "eth_requestAccounts" });
    
     } catch (error) {
    
       console.log(error);
    
     }
    
 }}
    
 </script>
    
 </html>

这是提供所需功能的完整代码段。 eth_requestAccounts源自Metamask官方指导手册。建议将文件命名为index.html并将其置于浏览器中打开,则会自然地引导至Metamask连接页面。

发送交易

我已经成功地连接到了Metamask钱包,并且恰如其分的时候准备发起一次交易。我们可以通过调用ethersjsweb3js等相关的包来实现与提供者的通信,并完成此次交易操作。一般来说,在JavaScript中执行一个函数或者发起一次交易的过程类似于以下所示:

复制代码
 const etheres = require("ethers")

    
  
    
 contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
    
 const abi = // some big javascript ABI here...
    
  
    
 const provider = new ethers.providers.JsonRpcProvider(/* alchemy or infura */)
    
 const wallet = new ethers.Wallet(/* Private key */, provider)
    
  
    
 const contract = new ethers.Contract(contractAddress, abi, wallet)
    
 const contractWithSigner = contract.connect(wallet)
    
 const transactionResponse = contract.someFunction()

请注意的是,在这种情况下, Metamask 同时扮演了两者的角色

复制代码
 const etheres = require("ethers")

    
  
    
 contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
    
 const abi = // some big javascript ABI here...
    
  
    
 const provider = new ethers.providers.Web3Provider(window.ethereum)
    
 const signer = provider.getSigner();
    
  
    
 const contract = new ethers.Contract(contractAddress, abi, signer)
    
 const contractWithSigner = contract.connect(wallet)
    
 const transactionResponse = contract.someFunction()

值得注意的是,在本次操作中仅中间两行进行了改动。具体而言,通过访问window.ethereum获取钱包后,我们采用了metamask作为签名者(signer),即使用该工具进行签名操作。

目前,在这个问题中存在一定的挑战性。由于我们的浏览器在处理require语法时出现困难(有时也会遇到import的问题),因此需要采取相应的措施来解决这些问题。

为了避免这里变成一篇前端介绍的文章,请参阅我的html-js-ethers-connect一例。该资源将指导您自行运行一个类似的示例。安装相关软件后即可开始操作。

安装Git

接下来,请按照README.md中的说明进行初始化操作。首先,在代码编辑器中编写HTML结构;其次,在另一个脚本中使用JavaScript编写逻辑;最后,在浏览器中向其发起交易请求。

你将拥有一个与智能合约一起工作的简约的前端!

5个最佳前端Web3的实践

没有特别的顺序

现在,让我们开始为全栈应用提供所需的工具。这些配置将包括:

  1. 如何初始化
  2. 极简的演示
  3. 真实世界的例子

在选择方案时,请优先考虑最适合您的那个!我们可以采用NextJS来完成这些功能。其中,ReactJS是最流行的前端框架之一,由于它是当前最流行的前端框架之一,因此我们可以基于它构建相应的功能模块.相较于传统的React.js版本而言,在我的实践中发现它更加便捷.不过您也可以根据需要选择Angular、Svelte或其他替代方案来实现相同的功能.

你可以在我的所有简约代码示例中找到full-stack-web3-metamask-connectors仓库中的相关资源,并从中查看所有相关的演示。

初始化一个基本的NextJS项目

为了帮助新手快速上手开发项目, 这些项目都将基于一个完整的基础架构展开. 为了顺利运行这些项目, 安装必要的开发环境工具包是必不可少的前提. 您需要下载并安装以下软件: Node.js框架 (可通过Node获取), Git版本控制工具 (详情请参阅Git), 以及Yarn依赖管理器 (获取方式请访问Yarn). 您可以通过官方入门指南快速掌握项目的开发流程.

运行以下命令:

复制代码
 yarn create next-app full-stack-web3

    
 cd full-stack-web3

当前构建了一个基础的项目架构,并已准备好进行开发测试。通过执行命令yarn dev将快速进入开发模式,并观察目前网站的整体布局如何。最后一步是删除所有初始阶段的 示例代码 并转向目标文件夹中的index.js文件,在其中清空现有内容以开始 fresh 的开发工作。

复制代码
 export default function Home() {

    
   return <div>Hi</div>;
    
 }

现在前端就显示一个 Hi

设置本地Hardhat区块链和合约

为了测试函数交互的实现需求, 我们必须确保能够使用区块链技术来发送交易, 并配套相应的智能合约系统。代码库已预先准备好在hardhat-simple-storage位置上的存储库结构, 你可以参考README.md文件进行配置, 或者在与前端设备不同的终端中运行指定程序完成部署操作。

复制代码
 git clone https://github.com/PatrickAlphaC/hardhat-simple-storage

    
 cd hardhat-simple-storage
    
 yarn
    
 yarn hardhat node

当前将启动本地区块链网络,并提供一组临时私钥账号。这些账号特别适用于部署 SimpleStorage 合约。该合约通过接收一个 uint256 类型的 _favoriteNumber 参数来获取数值信息,并将其存储到一个公共变量中。具体合约代码可以在 SimpleStorage.sol 文件中找到详细信息。

用本地区块链设置你的MetaMask

为了实现与本地区块链的连接, 我们需要将Metamask与此地区的区块链系统进行关联配置, 这样便能够迅速发送交易并进行测试. 本地区块链与真实区块链具有相似性, 但它是一个我们完全掌控的操作系统. 如果有兴趣的话, 则可以选择使用公 test network(测试网)而不必进行这一步骤, 但请注意在处理这些交易时需要耗费大量时间——这对于所有人来说都是不划算的.

在运行中的区块链节点终端设备上,你会观察到一个类似的结果:程序启动了HTTP和WebSocketJSON-RPC服务端口8545,并通过地址http://127.0.0.1:8545\提供相关服务。\r\n\r\n这个URL即为RPC URL,在区块链生态系统中扮演着与AlCHEMY相似的角色。\r\n

在Metamask应用程序中,请不建议在拥有真实资金支持的情况下进行开发操作。为了确保安全配置,请您建立一个新的浏览器配置文件(Profile),或者下载一个带有 Metamask 插件 的其他浏览器以实现安全配置。单击位于顶部栏中的 网络设置 按钮 ,然后选择添加网络 选项

按照指定内容配置参数后,请执行以下操作:首先点击保存按钮以确认设置;随后请确保切换至该网络(建议从网络下拉菜单中选择刚才配置好的那个网络)。

现在,点击右上方的大圆圈(账号),然后点击 导入账户(import account)

随后,请根据yarn hardhat node命令的输出信息创建一个新的私钥。随后,在本地网络环境中你会看到一个新账户,并观察到其存储的少量测试以太币(ETH)。 Metamask界面应当呈现如下状态:例如图1所示。

然后我们就可以开始了 :)

特别提示:遇到nonce处于关闭状态或交易无法正常进行的情况时,请按照以下方法操作以解决问题:在metamask软件中进入设置菜单并选择高级选项模块;在此菜单下找到并勾选目标账户信息后完成重置流程即可解除nonce状态

使用原始 Ethers

此处是完整的代码位置链接:[连接此仓库](https://github.com/PatrickAlphaC/nextjs-ethers-metamask-connect "此处是完整的代码位置链接")

此处是完整的代码位置链接:[连接此仓库](https://github.com/PatrickAlphaC/nextjs-ethers-metamask-connect "完整代码位置")

最简单的办法就是利用一些你已经熟悉的手法进行操作。例如,在这种情况下我们可以将从HTML设置中复制粘贴的内容移动至index.js文件中

复制代码
 import styles from "../styles/Home.module.css";

    
 import { ethers } from "ethers";
    
 import { useEffect, useState } from "react";
    
 export default function Home() {
    
   const [isConnected, setIsConnected] = useState(false);
    
   const [hasMetamask, setHasMetamask] = useState(false);
    
   const [signer, setSigner] = useState(undefined);
    
 useEffect(() => {
    
     if (typeof window.ethereum !== "undefined") {
    
       setHasMetamask(true);
    
     }
    
   });
    
 async function connect() {
    
     if (typeof window.ethereum !== "undefined") {
    
       try {
    
     await ethereum.request({ method: "eth_requestAccounts" });
    
     setIsConnected(true);
    
     const provider = new ethers.providers.Web3Provider(window.ethereum);
    
     setSigner(provider.getSigner());
    
       } catch (e) {
    
     console.log(e);
    
       }
    
     } else {
    
       setIsConnected(false);
    
     }
    
   }
    
 async function execute() {
    
     if (typeof window.ethereum !== "undefined") {
    
       const contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
    
       const abi = [
    
     {
    
       inputs: [
    
         {
    
           internalType: "string",
    
           name: "_name",
    
           type: "string",
    
         },
    
         {
    
           internalType: "uint256",
    
           name: "_favoriteNumber",
    
           type: "uint256",
    
         },
    
       ],
    
       name: "addPerson",
    
       outputs: [],
    
       stateMutability: "nonpayable",
    
       type: "function",
    
     },
    
     {
    
       inputs: [
    
         {
    
           internalType: "string",
    
           name: "",
    
           type: "string",
    
         },
    
       ],
    
       name: "nameToFavoriteNumber",
    
       outputs: [
    
         {
    
           internalType: "uint256",
    
           name: "",
    
           type: "uint256",
    
         },
    
       ],
    
       stateMutability: "view",
    
       type: "function",
    
     },
    
     {
    
       inputs: [
    
         {
    
           internalType: "uint256",
    
           name: "",
    
           type: "uint256",
    
         },
    
       ],
    
       name: "people",
    
       outputs: [
    
         {
    
           internalType: "uint256",
    
           name: "favoriteNumber",
    
           type: "uint256",
    
         },
    
         {
    
           internalType: "string",
    
           name: "name",
    
           type: "string",
    
         },
    
       ],
    
       stateMutability: "view",
    
       type: "function",
    
     },
    
     {
    
       inputs: [],
    
       name: "retrieve",
    
       outputs: [
    
         {
    
           internalType: "uint256",
    
           name: "",
    
           type: "uint256",
    
         },
    
       ],
    
       stateMutability: "view",
    
       type: "function",
    
     },
    
     {
    
       inputs: [
    
         {
    
           internalType: "uint256",
    
           name: "_favoriteNumber",
    
           type: "uint256",
    
         },
    
       ],
    
       name: "store",
    
       outputs: [],
    
       stateMutability: "nonpayable",
    
       type: "function",
    
     },
    
       ];
    
       const contract = new ethers.Contract(contractAddress, abi, signer);
    
       try {
    
     await contract.store(42);
    
       } catch (error) {
    
     console.log(error);
    
       }
    
     } else {
    
       console.log("Please install MetaMask");
    
     }
    
   }
    
   return (
    
     <div>
    
       {hasMetamask ? (
    
     isConnected ? (
    
       "Connected! "
    
     ) : (
    
       <button onClick={() => connect()}>Connect</button>
    
     )
    
       ) : (
    
     "Please install metamask"
    
       )}
    
 {isConnected ? <button onClick={() => execute()}>Execute</button> : ""}
    
     </div>
    
   );
    
 }

为此, 我们增加了若干功能模块, 以便于在无需Metamask的情况下显示提示信息。您将能够使用诸如useStateuseEffect之类的钩子函数, 这些钩子函数可帮助实现状态管理等功能。通过观看这个Fireship视频链接(https://www.youtube.com/watch?v=TNhaISOUy6Q)或访问react docs.中的相关内容, 您可以深入了解这些钩子函数的具体用途。值得注意的是, 在不使用这些钩子的情况下, 该应用仍能正常运行, 但并不能实现状态持久化。

优点

  • 直接使用Ethers对UI进行最精细的控制

缺点

不得不编写大量的自定义代码,并且包含Contexts
然而增加更多钱包连接的处理可能会变得较为复杂。

使用示例

The Nader Dabit Primer

此外,在下面的案例中, 我计划从另一个文件中引入abi以使文章内容更加简洁明了

使用 Web3Modal

核心功能:完整代码在这里

核心功能:完整代码到这里

将基于EVM的技术应用至区块链系统的开发中并将其接入钱包的主要采用方法是Walletconnect这一知名平台。所有展示案例(包括原始Ethers的应用程序)都可以与其建立连接并非唯一的选择是Web3Modal它只是一个参考选项。该团队开发了一个独特的工具即Web3Modal它支持通过一个统一框架实现与各种Provider(包括Ledger、WalletConnect、Torus、Coinbase Wallet等)的集成这一创新设计特别适合用于构建跨平台的应用程序并提升用户体验。

我们只需要导入这个包,之后index.js可能看起来像这样:

复制代码
 import styles from "../styles/Home.module.css";

    
 import Web3Modal from "web3modal";
    
 import { useState, useEffect } from "react";
    
 import { ethers } from "ethers";
    
 import WalletConnectProvider from "@walletconnect/web3-provider";
    
 import { abi } from "../constants/abi";
    
 let web3Modal;
    
 const providerOptions = {
    
   walletconnect: {
    
     package: WalletConnectProvider, // required
    
     options: {
    
       rpc: { 42: process.env.NEXT_PUBLIC_RPC_URL }, // required
    
     },
    
   },
    
 };
    
 if (typeof window !== "undefined") {
    
   web3Modal = new Web3Modal({
    
     cacheProvider: false,
    
     providerOptions, // required
    
   });
    
 }
    
 export default function Home() {
    
   const [isConnected, setIsConnected] = useState(false);
    
   const [hasMetamask, setHasMetamask] = useState(false);
    
   const [signer, setSigner] = useState(undefined);
    
 useEffect(() => {
    
     if (typeof window.ethereum !== "undefined") {
    
       setHasMetamask(true);
    
     }
    
   });
    
 async function connect() {
    
     if (typeof window.ethereum !== "undefined") {
    
       try {
    
     const web3ModalProvider = await web3Modal.connect();
    
     setIsConnected(true);
    
     const provider = new ethers.providers.Web3Provider(web3ModalProvider);
    
     setSigner(provider.getSigner());
    
       } catch (e) {
    
     console.log(e);
    
       }
    
     } else {
    
       setIsConnected(false);
    
     }
    
   }
    
 async function execute() {
    
     if (typeof window.ethereum !== "undefined") {
    
       const contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
    
       const contract = new ethers.Contract(contractAddress, abi, signer);
    
       try {
    
     await contract.store(42);
    
       } catch (error) {
    
     console.log(error);
    
       }
    
     } else {
    
       console.log("Please install MetaMask");
    
     }
    
   }
    
 return (
    
     <div>
    
       {hasMetamask ? (
    
     isConnected ? (
    
       "Connected! "
    
     ) : (
    
       <button onClick={() => connect()}>Connect</button>
    
     )
    
       ) : (
    
     "Please install metamask"
    
       )}
    
 {isConnected ? <button onClick={() => execute()}>Execute</button> : ""}
    
     </div>
    
   );
    
 }

观察到的是,在配置中我们设置了某些providerOptions变量来告知前端系统兼容多种钱包地址。此外,我们需要包含多个区块链网络,并指定一个NEXT_PUBLIC_RPC_URL变量来连接到主区块链。该变量指向该RPC URL地址,并通过此路径连接到主区块链网络。采用walletconnect技术后,则无需依赖metamasks内置的区块链节点。

优点

  • 易于整合多个钱包
  • Ethers 很好集成

缺点

  • 仍然没有内置的上下文组件

使用示例

如果想深入了解Web3Modal、区块链等领域的最新动态,请访问Scaffold-ETH平台。这是一个卓越的学习资源,在该平台上由 Austin Griffith 开发,并旨在帮助开发者深入分析并解构并行计算的最佳实践。

Moralis

此处提供了完整的源码链接:[完整的源码库](https://github.com/PatrickAlphaC/nextjs-moralis-metamask-connect "此处提供完整的源码库")。

Moralis(或更具体地说,react-moralis) 是首个引入情境管理组件的应用程序 ,无疑是非常有用的 。 因为它使得整个应用能够轻松地在组件间共享状态 , 这正是我们传递Metamask授权所需采取的重要措施 。 该工具无疑是非常有用的

Moralis是由Ivan on Tech及其团队开发的项目平台。它不仅能够帮助开发者连接至Metamask生态,并且还能支持构建其他类型的底层服务(包括那些可能需要全栈开发能力的应用)。像Etherscan和Opensea这样的代表性平台都是基于区块链技术的典型应用场景。由于在区块链上实现大量功能往往需要消耗大量Gas代币,在链上进行此类操作可能会面临资源限制的问题。因此仍需依赖一个后台系统和数据库来存储相关数据信息。

进而 args = (args,) if isinstance(args, tuple) else args\n此外, you can utilize the following code snippets to implement the system efficiently:

复制代码
 import styles from "../styles/Home.module.css";

    
 import { useMoralis, useWeb3Contract } from "react-moralis";
    
 import { abi } from "../constants/abi";
    
 import { useState, useEffect } from "react";
    
 export default function Home() {
    
   const [hasMetamask, setHasMetamask] = useState(false);
    
   const { enableWeb3, isWeb3Enabled } = useMoralis();
    
   const { data, error, runContractFunction, isFetching, isLoading } =
    
     useWeb3Contract({
    
       abi: abi,
    
       contractAddress: "0x5FbDB2315678afecb367f032d93F642f64180aa3",   // your contract address here
    
       functionName: "store",
    
       params: {
    
     _favoriteNumber: 42,
    
       },
    
     });
    
 useEffect(() => {
    
     if (typeof window.ethereum !== "undefined") {
    
       setHasMetamask(true);
    
     }
    
   });
    
 return (
    
     <div>
    
       {hasMetamask ? (
    
     isWeb3Enabled ? (
    
       "Connected! "
    
     ) : (
    
       <button onClick={() => enableWeb3()}>Connect</button>
    
     )
    
       ) : (
    
     "Please install metamask"
    
       )}
    
 {isWeb3Enabled ? (
    
     <button onClick={() => runContractFunction()}>Execute</button>
    
       ) : (
    
     ""
    
       )}
    
     </div>
    
   );
    
 }

Moralis展示了一个强大的hook函数useWeb3Contract$,显著简化了获取状态以及与合约的交互流程,并且无需以太坊代币。

Moralis 还提供的enableWeb3函数代替了自己编写的connect函数。

此外,在_app.js中,需要用一个Context提供者来包装整个应用程序:

复制代码
 import "../styles/globals.css";

    
 import { MoralisProvider } from "react-moralis";
    
  
    
 function MyApp({ Component, pageProps }) {
    
   return (
    
     <MoralisProvider initializeOnMount={false}>
    
       <Component {...pageProps} />
    
     </MoralisProvider>
    
   );
    
 }
    
  
    
 export default MyApp;

Morlais内置属性提供多种配置选择,在实际应用中可以根据需求灵活调整。具体来说,在开发过程中你可以通过数据库配置前端功能;但如果只需要使用钩子与函数而不涉及服务器初始化,则建议将initializeOnMount参数设为false;等到后续开发中需要时再进行服务器配置以避免不必要的开销。

优点

该系统能够提供上下文信息的来源,并支持与智能合约的交互。
通过引入后端技术可以实现对前端功能的扩展。

缺点

  • 必须手动添加自己的钱包

真实案例

Web3-React

完整代码在这里

Uniswap项目的联合开发者Noah Zinsmeister及其团队开发了一个叫做web3-react的优秀软件包。该软件包是多个顶级项目中被广泛采用的主要工具之一。它拥有一个上下文组件管理器以及一些强大的功能钩子(Hook),这些功能钩子帮助你快速上手并开始工作。此外还内置了一些用于连接web3钱包的功能模块

以下是index.js修改后的代码:

复制代码
 import styles from "../styles/Home.module.css";

    
 import { useWeb3React } from "@web3-react/core";
    
 import { InjectedConnector } from "@web3-react/injected-connector";
    
 import { abi } from "../constants/abi";
    
 import { useState, useEffect } from "react";
    
 import { ethers } from "ethers";
    
 export const injected = new InjectedConnector();
    
 export default function Home() {
    
   const [hasMetamask, setHasMetamask] = useState(false);
    
 useEffect(() => {
    
     if (typeof window.ethereum !== "undefined") {
    
       setHasMetamask(true);
    
     }
    
   });
    
 const {
    
     active,
    
     activate,
    
     chainId,
    
     account,
    
     library: provider,
    
   } = useWeb3React();
    
 async function connect() {
    
     if (typeof window.ethereum !== "undefined") {
    
       try {
    
     await activate(injected);
    
     setHasMetamask(true);
    
       } catch (e) {
    
     console.log(e);
    
       }
    
     }
    
   }
    
 async function execute() {
    
     if (active) {
    
       const signer = provider.getSigner();
    
       const contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
    
       const contract = new ethers.Contract(contractAddress, abi, signer);
    
       try {
    
     await contract.store(42);
    
       } catch (error) {
    
     console.log(error);
    
       }
    
     } else {
    
       console.log("Please install MetaMask");
    
     }
    
   }
    
 return (
    
     <div>
    
       {hasMetamask ? (
    
     active ? (
    
       "Connected! "
    
     ) : (
    
       <button onClick={() => connect()}>Connect</button>
    
     )
    
       ) : (
    
     "Please install metamask"
    
       )}
    
 {active ? <button onClick={() => execute()}>Execute</button> : ""}
    
     </div>
    
   );
    
 }

_app.js代码:

复制代码
 import "../styles/globals.css";

    
 import { Web3ReactProvider } from "@web3-react/core";
    
 import { Web3Provider } from "@ethersproject/providers";
    
 const getLibrary = (provider) => {
    
   return new Web3Provider(provider);
    
 };
    
 function MyApp({ Component, pageProps }) {
    
   return (
    
     <Web3ReactProvider getLibrary={getLibrary}>
    
       <Component {...pageProps} />
    
     </Web3ReactProvider>
    
   );
    
 }
    
 export default MyApp;

如你所知,在当前阶段我们依然通过Ethers进行智能合约的交互。然而为了扩展我们的钱包选项并提升灵活性与可扩展性需求的实现程度(即实现更多钱包Provider的功能)我们将采用钩子函数以启用Metamask以及任何其他钱包Provider

优点

  • 上下文提供者
  • 内置与智能合约交互的功能
  • 内置钱包连接

缺点

  • 不像Web3-modal那样简便易用
    • 必须开发或集成自定义 hook 以实现特定功能

真实案例

使用Dapp

完整的代码实现细节请参考以下链接地址:[此处将具体链接地址填入]

该测试框架由Ethworks及其合作伙伴共同开发,并得到了硬帽团队的支持。该框架基于Waffle平台构建,并提供了完整的 hooks 和工具集合。此外还支持所有钩子和工具包,并提供了一个上下文管理器。

下面是使用Ethworks后index.js 的代码:

复制代码
 import styles from "../styles/Home.module.css";

    
 import { useEthers, useContractFunction } from "@usedapp/core";
    
 import { useState, useEffect } from "react";
    
 import { ethers } from "ethers";
    
 import { abi } from "../constants/abi";
    
 export default function Home() {
    
   const { activateBrowserWallet, account } = useEthers();
    
   const [hasMetamask, setHasMetamask] = useState(false);
    
 useEffect(() => {
    
     if (typeof window.ethereum !== "undefined") {
    
       setHasMetamask(true);
    
     }
    
   });
    
  
    
 async function connect() {
    
     await activateBrowserWallet();
    
   }
    
   const contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
    
   const contract = new ethers.Contract(contractAddress, abi);
    
 const { send, state } = useContractFunction(contract, "store", {
    
     transactionName: "store",
    
   });
    
 useEffect(() => {
    
     console.log(`State: ${state.status}`);
    
   }, [state]);
    
 return (
    
     <div>
    
       {hasMetamask ? (
    
     account ? (
    
       "Connected! "
    
     ) : (
    
       <button onClick={() => connect()}>Connect</button>
    
     )
    
       ) : (
    
     "Please install metamask"
    
       )}
    
 {account ? <button onClick={() => send(42)}>Execute</button> : ""}
    
     </div>
    
   );
    
 }

_app.js如下:

复制代码
 import "../styles/globals.css";

    
 import { DAppProvider } from "@usedapp/core";
    
 const config = {
    
   multicallAddresses: ["0x5FbDB2315678afecb367f032d93F642f64180aa3"],
    
 };
    
 function MyApp({ Component, pageProps }) {
    
   return (
    
     <DAppProvider config={config}>
    
       <Component {...pageProps} />
    
     </DAppProvider>
    
   );
    
 }
    
 export default MyApp;

将参数发送给应用程序以配置其支持的区块链及其他连接属性。如同Moralis, useDapp提供激活metamask/浏览器钱包的功能, 并通过类似于useContractFunction等钩子函数与智能合约交互(无需使用ethers)。

优点

  • 上下文提供者
  • 内置智能合约交互功能

缺点

  • 不像web3modal那样容易设置钱包
  • 没有内置数据库的选项

真实案例

基于 brownie 的 Defi 利益再投资项目位于 GitHub 的 PatrickAlphaC 仓库

小结

每个工具都具有独特的优势与不足,在综合考虑个人偏好和显著的需求后进行选择会更加高效

编码愉快!

全部评论 (0)

还没有任何评论哟~