用Ethers.js构建一个DApp

2,862 阅读8分钟

简介

区块链、以太坊、DApps、智能合约......如果你在互联网上,你有可能在某个地方看到这些词,而且可能没有完全理解它们是什么或它们背后的技术如何运作。这篇文章为你解决了这个问题。

在本教程中,我们将学习区块链的一些概念,以及学习如何用Ethers.js构建我们的第一个DApp。

我们将建立什么

我们将致力于创建一个DApp x Defi,显示我们当前的USDT余额,允许我们将USDT转移到其他钱包,最后允许我们查询我们的USDT交易历史。

最终的输出将是这样的。

Ethers.js DApp Final Output

先决条件

要跟上这篇文章,你将需要。

  • 对HTML的基本了解
  • 对JavaScript和DOM的基本理解
  • 安装Metamask钱包

本文是如何组织的

本文分为三个部分。

如果你已经熟悉了区块链和DApp的工作方式,你可以直接跳到Getting started with Ethers.js部分。

区块链和以太坊的核心概念

维基百科将区块链定义为 "一个不断增长的记录列表,称为区块,使用密码学连接在一起。"你可以把区块链看作是一个数据库,但与初级数据库不同,区块链是去中心化的--即没有中央组织控制其活动。

此外,区块链是不可改变的,也就是说,任何存储在上面的数据在创建后都不能被修改。

2009年,中本聪通过创建世界上第一个加密货币比特币,普及了区块链。比特币在区块链上开发,使其成为第一个不受政府或某些组织控制的点对点数字货币。而在比特币问世几年后,19岁的Vitalik Buterin创建了以太坊。

什么是以太坊?

以太坊是一个去中心化的,具有智能合约和DApp开发功能的开源区块链。以太坊为开发者提供了一个平台,用于创建交换信息和服务的通用应用程序,其数据不能在区块链上被操纵或改变。

除了以太坊区块链,Vitalik和他的团队还创建了一个原生的加密货币。以太币(简称ETH),它与比特币类似,但也用于在区块链上运行交易,当与DApps互动时。

智能合约

智能合约是在以太坊区块链上运行的程序。你可以把智能合约看作是一个普通的以太坊账户,上面存储了一些以太坊,可以通过以太坊网络进行交易。但与普通账户不同,智能合约不是由用户控制的。他们而是被编程,部署到网络上,并自动执行任务。

DApps

去中心化应用程序(简称DApps)是为与区块链互动而建立的应用程序。与智能合约相比,DApps并不明确存在于区块链上;它们是为了与智能合约互动而建立的。

现在你已经掌握了常见的区块链术语,让我们直接开始用Ethers.js创建我们的第一个DApp吧

开始使用Ethers.js

Ethers.js是一个JavaScript库,允许开发人员轻松地与以太坊区块链及其生态系统互动。

安装

Ethers.js作为一个npm包,可以通过运行来安装。

npm install --save ethers

或者像下面这样将其CDN包含在一个HTML文档中。

<script
  src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"
  type="application/javascript">
</script>

如果你正在使用Node.js,你可以在你的项目中导入Ethers包。

const { ethers } = require("ethers");

对于一个基于ES6或TypeScript的项目,你可以用以下方式导入它。

import { ethers } from "ethers";

否则,如果你已经在你的标记中包含了CDN,就不需要额外的努力来导入,因为Ethers包会自动加载。

在使用Ethers.js时,你会遇到一些常见的术语,包括。

  • 提供者 - 这是Ethers.js中的一个类,提供对Ethereum区块链及其状态的抽象只读访问。
  • 签名者 - 这是Ethers中的一个类,可以访问你的私钥。这个类负责签署消息,并授权交易,包括从你的账户中收取以太币来执行操作。
  • 合约 - 这个类负责连接到以太坊网络上的特定合约

连接到MetaMask

MetaMask是一个存储加密货币的钱包,它也是连接到基于区块链的应用程序的一个网关。如果你还没有,请到他们的下载页面,为你喜欢的浏览器下载网络扩展。

设置好MetaMask后,我们可以访问全球以太坊API,可以通过window.ethereum

你可以通过创建一个新的Web3提供者,并将全球以太坊API(window.ethereum) 作为参数,像下面这样,轻松地用Ethers.js连接到MetaMask。

const provider = new ethers.providers.Web3Provider(window.ethereum)

要尝试这一点,创建一个新的index.html 文件并添加以下代码。

<!DOCTYPE html>
<html lang="en">
  <head>
    <script
      src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"
      type="application/javascript"
    ></script>
    <script>
      const provider = new ethers.providers.Web3Provider(
        window.ethereum,
        "any"
      );
      await provider.send("eth_requestAccounts", []);
      const signer = provider.getSigner();

      (async function () {
        let userAddress = await signer.getAddress();
        document.getElementById("wallet").innerText =
          "Your wallet is " + userAddress;
      })();
    </script>
  </head>
  <body>
    <div id="wallet"></div>
  </body>
</html>

在上面的代码中,在包括Ethers.js CDN之后,我们创建了一个与MetaMask的连接,并将其分配给一个常量变量provider ,之后,我们创建了一个自激发的异步函数,我们用它来获取我们连接的MetaMask账户。

运行该代码将提示一个新的MetaMask连接,如下图所示,一旦连接成功,你应该看到你的钱包地址显示在页面上。

Ethers.js Wallet Address Dispaly

查询以太坊区块链

通过provider 类,我们可以对区块链数据进行只读访问,通过这个,我们可以获取区块链的当前状态,读取历史日志,以及更多。

例如,我们可以使用异步的getBlockNumber() 方法来获取以太坊区块链中的当前挖出的区块。

const provider = new ethers.providers.Web3Provider(window.ethereum)
async function getCurrentBlock() {
  let currentBlock = await provider.getBlockNumber();
  console.log(currentBlock);
}

getCurrentBlock();

// returns 13344639

或者使用getBalance() 方法,获得指定地址中可用的以太币数量。

const provider = new ethers.providers.Web3Provider(window.ethereum)

async function getBalance(wallet) {
  let balance = await provider.getBalance(wallet);
  // we use the code below to convert the balance from wei to eth
  balance = ethers.utils.formatEther(balance);
  console.log(balance);
}

getBalance("0xC7CF72918D9a7101D2333538686E389b15ad8F2E");
// returns 0.3

请访问这里,了解提供者类下所有可用方法的完整参考

签署消息

在区块链上签署消息意味着创建数字签名。这些数字签名被用来证明一个地址的所有权,而不暴露其私钥。

通过使用签名者类中的 signMessage() 方法,我们可以很容易地用Ethers.js做到这一点。

let mySignature = await signer.signMessage("Some custom message");

构建我们的DApp

要开始工作,请访问这个GitHub仓库,并按照仓库README的指示,在你的本地机器上设置项目前端分支。这个仓库包含了我们在创建DApp时需要的所有文件,以及用户界面的标记代码,但还没有添加功能。

如果你按照安装过程成功,并运行index.html 文件,你应该有一个类似于下面图片的输出。

DApp Installation Process

获得免费的ETH

为了在以太坊区块链上进行任何交易,我们将被收取一些以太坊,也就是所谓的气体费用。而且,由于这是一个测试项目,我们不希望支付实际的钱来运行交易。要获得免费的测试ETH,请打开MetaMask,并将网络改为 ropsten测试网络,复制你的钱包地址,最后在ropsten faucet提交你的钱包,就可以获得0.3的免费ETH。

铸造USDC

由于我们正在建立一个允许我们转移USDC的DApp,我们想先从我们的ETH余额中为自己铸造一些。要做到这一点,请打开 /script/mint-usdc.js 文件,并用以下内容进行更新。

const provider = new ethers.providers.Web3Provider(window.ethereum, "any");

const usdc = {
  address: "0x68ec573C119826db2eaEA1Efbfc2970cDaC869c4",
  abi: [
    "function gimmeSome() external",
    "function balanceOf(address _owner) public view returns (uint256 balance)",
    "function transfer(address _to, uint256 _value) public returns (bool success)",
  ],
};

async function mintUsdc() {
  await provider.send("eth_requestAccounts", []);
  const signer = provider.getSigner();
  let userAddress = await signer.getAddress();
  const usdcContract = new ethers.Contract(usdc.address, usdc.abi, signer);

  const tx = await usdcContract.gimmeSome({ gasPrice: 20e9 });
  console.log(`Transaction hash: ${tx.hash}`);

  const receipt = await tx.wait();
  console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
}

接下来,保存该文件并运行我们根文件夹中可用的mint-usdc.html 文件。现在,如果你点击这个页面上的MintNow 按钮,这应该会提示一个新的MetaMask请求,确认这个请求将为我们的钱包铸造10个USDC,以及用于请求的交易哈希和气体费用打印在我们的控制台。

显示USDC余额

现在我们已经铸造了一些USDC,让我们继续并在我们的标记中的指定区域显示它。要做到这一点,打开/script/app.js ,用下面的代码更新它。

const provider = new ethers.providers.Web3Provider(window.ethereum, "any");

const usdc = {
  address: "0x68ec573C119826db2eaEA1Efbfc2970cDaC869c4",
  abi: [
    "function name() view returns (string)",
    "function symbol() view returns (string)",
    "function gimmeSome() external",
    "function balanceOf(address _owner) public view returns (uint256 balance)",
    "function transfer(address _to, uint256 _value) public returns (bool success)",
  ],
};

async function main() {
  await provider.send("eth_requestAccounts", []);
  const signer = provider.getSigner();
  let userAddress = await signer.getAddress();
  document.getElementById("userAddress").innerText = userAddress;

  const usdcContract = new ethers.Contract(usdc.address, usdc.abi, signer);

  let usdcBalance = await usdcContract.balanceOf(userAddress);
  usdcBalance = ethers.utils.formatUnits(usdcBalance, 6);
  document.getElementById("usdcBalance").innerText = usdcBalance;
}
main();

这里,我们在传递地址时使用了USDC ABI中的balanceOf() 函数。id 就像在开始部分提到的那样,余额将以Wei为单位返回,所以我们使用Ethers.js函数将其从Wei转换为ETH,并将结果显示在我们的divusdcBalance

现在,如果我们运行index.html ,我们应该看到我们的USDC余额以及我们的钱包显示在他们指定的部分,像下面这样。

DApp USDC Balance

添加转账功能

最后一步是添加转账功能。打开script/transfer-usdc.js ,用下面的代码更新其内容。

async function transferUsdc() {
    let receiver = document.getElementById("receiver").value;
    let amount = document.getElementById("amount").value;
    let response;
  
    await provider.send("eth_requestAccounts", []);
    const signer = provider.getSigner();
    let userAddress = await signer.getAddress();
  
    const usdcContract = new ethers.Contract(usdc.address, usdc.abi, signer);
  
    try {
      receiver = ethers.utils.getAddress(receiver);
    } catch {
      response = `Invalid address: ${receiver}`;
      document.getElementById("transferResponse").innerText = response;
      document.getElementById("transferResponse").style.display = "block";
    }
  
    try {
      amount = ethers.utils.parseUnits(amount, 6);
      if (amount.isNegative()) {
        throw new Error();
      }
    } catch {
      console.error(`Invalid amount: ${amount}`);
      response = `Invalid amount: ${amount}`;
      document.getElementById("transferResponse").innerText = response;
      document.getElementById("transferResponse").style.display = "block";
    }
  
    const balance = await usdcContract.balanceOf(userAddress);
  
    if (balance.lt(amount)) {
      let amountFormatted = ethers.utils.formatUnits(amount, 6);
      let balanceFormatted = ethers.utils.formatUnits(balance, 6);
      console.error(
        `Insufficient balance receiver send ${amountFormatted} (You have ${balanceFormatted})`
      );
  
      response = `Insufficient balance receiver send ${amountFormatted} (You have ${balanceFormatted})`;
      document.getElementById("transferResponse").innerText = response;
      document.getElementById("transferResponse").style.display = "block";
    }
    let amountFormatted = ethers.utils.formatUnits(amount, 6);
  
  
    response = `Transferring ${amountFormatted} USDC receiver ${receiver.slice(
      0,
      6
    )}...`;
    document.getElementById("transferResponse").innerText = response;
    document.getElementById("transferResponse").style.display = "block";
  
    const tx = await usdcContract.transfer(receiver, amount, { gasPrice: 20e9 });
    document.getElementById(
      "transferResponse"
    ).innerText += `Transaction hash: ${tx.hash}`;
  
    const receipt = await tx.wait();
    document.getElementById(
      "transferResponse"
    ).innerText += `Transaction confirmed in block ${receipt.blockNumber}`;
  }

代码解释

该代码几乎是不言自明的。首先,我们从我们的表单中得到所需的输入(接收者钱包和金额),在创建我们的合同后,我们检查输入的地址是否是一个有效的地址,也检查用户是否有足够的金额。最后,使用ABI的transfer() 方法,我们启动了转账。

我们的应用程序已经准备好了!

你可以在MetaMask上创建一个新的账户,然后向其中转入一些USDC。

另外,完整的源代码可以在GitHub上找到,以防止你错过任何步骤。

总结

在本教程中,我们不仅学习了区块链,还学习了如何使用Ethers.js在以太坊区块链上创建我们的第一个去中心化应用程序(DApp)。

The postBuilding a DApp with Ethers.jsappeared first onLogRocket Blog.