简介
区块链、以太坊、DApps、智能合约......如果你在互联网上,你有可能在某个地方看到这些词,而且可能没有完全理解它们是什么或它们背后的技术如何运作。这篇文章为你解决了这个问题。
在本教程中,我们将学习区块链的一些概念,以及学习如何用Ethers.js构建我们的第一个DApp。
我们将建立什么
我们将致力于创建一个DApp x Defi,显示我们当前的USDT余额,允许我们将USDT转移到其他钱包,最后允许我们查询我们的USDT交易历史。
最终的输出将是这样的。
先决条件
要跟上这篇文章,你将需要。
- 对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连接,如下图所示,一旦连接成功,你应该看到你的钱包地址显示在页面上。
查询以太坊区块链
通过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 文件,你应该有一个类似于下面图片的输出。
获得免费的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,并将结果显示在我们的div ,usdcBalance 。
现在,如果我们运行index.html ,我们应该看到我们的USDC余额以及我们的钱包显示在他们指定的部分,像下面这样。
添加转账功能
最后一步是添加转账功能。打开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.