使用Web3.j开发你的第一个Dapp

使用 Web3.j 开发你的第一个 Dapp

Web3js 简介


大量的加密货币的出现给数字支付带来新的希望,区跨链使这一切成为可能。很难想象去中心化货币的意义。Dapp 代表了互联网的全新愿景,完全不同于当前互联网的迭代。加密货币(或可编程代币)和智能合约等数字资产是去中心化应用(DApps)的核心组成部分,因为他们部署在区块链上。Web3.js 是一个库的集合,允许你与本地或者远程的以太坊节点交互。使用 HTTP 或 IPC 连接是开发 dapps 的骨干。

在本文中,我们将尝试使用 Web3js 构建一个去中心化 APP。

开始

今天,我们试图了解 Dapps 和 Web3js 基础知识。

目录

让我们从基本术语开始 — 区块链、去中心化应用、以太坊、智能合约以及 web3js。

区块链

区块链是一个可审计的数据库。只能在数据库中添加数据,但不能删除或更改数据。数据能被定期的添加到被称为块(blocks)的数据库中。顾名思义,一些列这些块连接在一起被称为区块链(Blockchain).

以太坊

以太坊是一个基于区块链域名的去中心化开平台,用于运行智能合约,即完全按照编程方式执行程序的应用程序,没有任何欺诈、第三方干预、审查或停机的可能。

智能合约

智能合约是指存储在区块链上的高级程序代码,编译成以太坊虚拟机,然后发布到以太坊区块链上执行。它让你能够在没有第三方干预的情况下进行值得信赖的交易;这些交易是可追踪的且不可逆的。通常用于创建和编写智能合约的编程语言是 Serpent, Solidity, Mutan, and LLL.

去中心化应用(Dapps)

去中心化应用(Dapp)是一种数字应用程序或程序,它驻留区块链或点对点计算机网络上执行,而不是单台计算机并且独立于任何人的权威控制。

Dapp 是去中心化应用的缩写,这意味着它不受单个组织控,在不受中央控制的公共、开源、去中心化环境中的区块链网络上运行。

https://miro.medium.com/max/1352/1*dd8jhjxugD-xLPbi5lGE_g.png

去中心化应用有三个主要组成部分:

  • 前端:从用户那里获取输入,并构建要发送到智能合约的请求。
  • 钱包:签署交易并发送到网络
  • 智能合约:这里是你编写 dapp 业务逻辑的地方

Dapp 有以下这些特性

  • 无停机时间 — 不会有停机时间或限制,因为它不像托管服务器那样依赖于单点故障,而是运行在点对点计算机网络
  • 透明 — 来自去中心化应用的数据存储在公共账本上,该账本以安全并透明的方式跟踪所有内容,确保没有人可以篡改它
  • 开源 — 开源 dApps 的代码可供查看。应为可以提供更多的输入,整个生态系统更具适应性、进步更快、发展更安全

Web3.js

Web3.js 是一个库的集合,允许你使用 HTTP、IPC 或 Websocket 与本地或远程以太坊节点进行交互。Web3.js 为我们提供了与 geth 通信的 Javascript API。它在内部使用 JSON-RPC 与 geth 通信。Web3.js 还可以与支持 JSON-RPC 的任何其他类型节点通信。它所有的 JSON-RPC API 公开为 JavaScript API。

它是如何运作的

图片来源 iotbl

图片来源 iotbl

Web3.js 使用 JSON-RPC 与以太坊区块链进行对话,JSON-RPC 代表“远程过程调用”协议。以太坊是一个点对点的节点网络,将所有数据、代码的副本存储在区块链上。Web3.js 允许我们使用 JSON-RPC 向单个以太坊节点发送请求,以便向网络读写数据。这点就像使用带有 JSON API 的 jQuery 来读写数据到 Web 服务器。

为了将 JavaScript 代码转换为 json-rpc 请求,web3.js 使用我们称为 provider 并实现了负责进行以太坊 RPC 方法调用的方法。Web3.js 上述提到的规范的实现,并使其在web3.providers、HttpProvider、WebsocketProviderIpcProvider下可以用

Web3.js 软件包

web3.js 附带五个主要的软件包:

  • web3.eth: 允许与以太坊区块链和以太坊智能合约进行交互。
  • web3.bzz: 允许与去中心化文件存储 Swarm 进行交互。
  • web3.shh: 允许与 Whisper 协议交互以进行广播。
  • web3.utils: 为以太坊 dapps 提供实用工具函数,例如将字符串转化为十六进制标识,将以太币(Ether)值转化为 Wei。
  • web3.*.net: 允许与以太坊节点的网络属性(如网络 ID 或节点数)进行交互。

第一个 APP

我们已经了解了基本的概念。现在,让我们深入了解第一个 dAPP

1. 设置环境

基本上,dAPP 要求你的机器安装 nodejs、truffle 和 ganache。

Node.js — 允许你使用 JavaScript 顺利的构建你的 Web 界面,并与区跨链交互

Truffle — 以太坊最受欢迎的开发框架

Ganache — Truffle 套件的一部分,它为你提供了一个本地网络,在设计精美的控制台上展示账户和交易

Solc — Solidity 编译器的 JavaScript 绑定。

1
npm install -g truffle

https://miro.medium.com/max/1400/0*OfjJ7kKWgVxDRJSb.png

https://miro.medium.com/max/1400/0*XnoJ3d8gZEMDJ9Vv.png

https://miro.medium.com/max/1294/0*AMsh9QBovNQhN4H6.png

1
npm install -g solc

https://miro.medium.com/max/1400/1*Jzj9NpTkAZURiyCXhACjkA.png

软件已经安装完成。现在,我们可以设置项目了。

让我们通过创建一个名为 dapp-demo 的项目文件夹,切换的文件夹下并使用 truffle 进行初始化。

1
2
3
mkdir dapp-demo
cd dapp-demo
truffle init

https://miro.medium.com/max/1400/1*eldAUUdN8LX5YMMczdMJaQ.png

这里我们将创建你需要的所有项目文件,你应该看到如下内容:

https://miro.medium.com/max/1400/1*6VY_pUmMTfgdFjV0a9SHVw.png

2. 编写合约

现在,是时候在你的项目中创建greeting合约。为此,需要创建一个文件名为 Greting.sol 并将该文件放置contracts文件夹下 ,并在其中添加如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Greeting {

string public greeting = "hello";
function sayHello() external view returns (string memory) {
return greeting;
}
function updateGreeting(string calldata _greeting) external {
greeting = _greeting;
}
}

3. 设置迁移

migrations文件夹中使用大于 1 的前缀创建一个新文件,例如,2_greeting_migartion.js部署HelloWorld.sol合约。

1
2
3
4
const Greeting = artifacts.require('Greeting');
module.exports = function (deployer) {
deployer.deploy(Greeting);
};

4. 编译和部署

使用下面命令编译你的项目

1
truffle compile

https://miro.medium.com/max/1400/1*_GA8q2TzJKMOsqC6h11cEg.png

编译成功之后在你的项目文件夹中将创建一个build文件夹。

现在,我们需要与 Truffle、Ganache 一起部署和测试你的合约。为此,打开 Ganache,选择”QuickStart” 并保持打开状态。

接下来,需要让 Truffle 知道部署在哪个网络上。转到truffle-config.js并通过取消deveopment部分的注释来修改网络详情。但请确保来自 Ganache 的 RPC 服务器端口与配置中的端口号一致。

https://miro.medium.com/max/1400/0*XNzCraTTgvQqHv1j.png

https://miro.medium.com/max/1400/1*XE_1emxqWw_dwV5ATjfSHA.png

使用下面命令部署合约

1
truffle deploy --network development

确认部署事务后,终端将反馈给你一些关于合约的详细信息,例如合约地址、区块信息。

https://miro.medium.com/max/1400/1*BUOfwfdQ_AGRTTXoA1XVog.png

5. 连接前端与智能合约

5.1 设置环境

在根目录下面创建一个名为client的新文件夹,并使用 npm 初始化。然后安装 web3.js 和 lite-server 依赖

1
2
3
4
5
6
mkdir client
cd client
npm init
npm install web3
npm install lite-server --save-dev
npm install jquery

https://miro.medium.com/max/1400/1*8ispMi4jvWqpZcQUtWIB_g.png

https://miro.medium.com/max/1530/1*HJC2wetULjjoETDeU7pQ8A.png

1_p4RFz_rtNGwkNZNt8nbjog.png

https://miro.medium.com/max/1532/1*lO2Zfc_pS2YDSY7bhX7lbQ.png

https://miro.medium.com/max/1532/1*1hFiTf2WxqFoDdtec4A8wA.png

创建一个名为src的文件夹并添加两个脚本:index.jsutils.js。你还需要在根文件夹(client 文件夹)下面创建index.html文件,并在其中添加如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Dapp Demo</title>
</head>
<body>
<h1>Dapp Demo</h1>
<br />
<h2></h2>
<form id="form">
<input id="input" type="text" />
<input type="submit" value="submit" />
</form>
<script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="node_modules/web3/dist/web3.min.js"></script>
<script type="text/javascript" src="src/utils.js"></script>
<script type="text/javascript" src="src/index.js"></script>
</body>
</html>

5.2 获取 web3 实例

一旦将 web3.js 作为项目中的依赖。你需要做的是使用 provider 实例去实例化 web3 对象,以便于享受 web3 提供的一切。在这个例子中,我们尝试使用 RPC 服务地址与 ganache 连接

打开 utils.js 文件并创建一个getWeb3()方法,该方法将从 ganache RPC 服务地址创建一个 web3js 实例。

1
2
3
4
5
6
7
8
9
10
11
12
const getWeb3 = () => {
return new Promise((resolve, reject) => {
window.addEventListener('load', async () => {
try {
const web3 = new Web3('http://127.0.0.1:7545');
resolve(web3);
} catch (error) {
reject(error);
}
});
});
};

为了创建一个 contract 实例,我们需要 contract ABI 及其地址。如果你查看了 build 目录下的 artifacts。你会发现一个名为Greeting.json的文件。如果你打开它,你会发现很多关于合约的信息,包含合约名、ABI 等。

client文件夹下创建一个名为contracts的新文件夹,并复制粘贴Greeting.json文件。

首先,我们使用 web3.eth.net.getId()获取 Ganache 连接到的网络 ID。然后,使用返回的 ID 从Greeting.json文件获取合约地址,该文件还为我们提供了合约 ABI,并使用 web3.eth.Contract 创建一个合约的实例。

在 utils.js 文件中创建一个getContract()方法,并在其中添加如下代码。

1
2
3
4
5
6
7
const getContract = async web3 => {
const data = await $.getJSON('./contracts/Greeting.json');
const netId = await web3.eth.net.getId();
const deployedNetwork = data.networks[netId];
const greeting = new web3.eth.Contract(data.abi, deployedNetwork && deployedNetwork.address);
return greeting;
};

5.4 与智能合约交互

一旦我们创建了一个合约的实例,我们可以开始使用myContract.methods.myMethod([arguments])调用他的方法,如文档所属。

如果调用的函数是纯函数(pure)或只读函数,则需要使用:

1
myContract.methods.myMethod([arguments]).call();

如果调用的函数需要修改状态,则需要使用:

1
myContract.methods.myMethod([arguments]).send();

最后,在index.js 文件中添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const displayGreeting = async (greeting, contract) => {
greeting = await contract.methods.sayHello().call();
$('h2').html(greeting);
};
const updateGreeting = (greeting, contract, accounts) => {
let input;
$('#input').on('change', e => {
input = e.target.value;
});
$('#form').on('submit', async e => {
e.preventDefault();
await contract.methods.updateGreeting(input).send({ from: accounts[0], gas: 40000 });
displayGreeting(greeting, contract);
});
};
async function greetingApp() {
const web3 = await getWeb3();
const accounts = await web3.eth.getAccounts();
const contract = await getContract(web3);
let greeting;
displayGreeting(greeting, contract);
updateGreeting(greeting, contract, accounts);
}
greetingApp();

最终项目结构如下所示,

https://miro.medium.com/max/1400/1*74lrT8qdVRSwaU7hMLvX-A.png

5.5 运行 app

开发工作已经完成。现在,我们在 package.json 文件中添加启动脚本后可以运行此项目。

1
2
3
4
"scripts": {
"test": "echo \"Error: no test specified \" && exit 1",
"start": "lite-server"
}

使用下面命令运行这个项目。

1
npm start

https://miro.medium.com/max/1400/1*z-xi4qOQgmPvTc3kenN-Ww.png

https://miro.medium.com/max/610/1*Fe09Ux2ukihPkn7e8824eg.png

https://miro.medium.com/max/608/1*eazlchwgFb1ehShst8T5HA.png

https://miro.medium.com/max/604/1*QgErQWK44r_4bOAOJtgMRQ.png

就这样, 你自己有 Web3js 的 Dapp。

感谢阅读全文。

如果你喜欢这篇文章,欢迎点赞打赏!

完整代码在https://github.com/codemaker2015/dapp-web3js-demo