使用 React、Solidity 和 Web3.js 构建真实世界的 dApp

简介: 网络技术的世界正在迅速向 Web 3.0 过渡,人们似乎已经厌倦了中心化的平台,数字隐私每天都受到大型组织的攻击和变卖。

网络技术的世界正在迅速向  Web 3.0  过渡,人们似乎已经厌倦了中心化的平台,数字隐私每天都受到大型组织的攻击和变卖。因此,需要一个解决方案,而 Web 3.0 似乎是目前最佳的答案。本文并不打算去介绍区块链和去中心化系统的专业知识,而是展示如何使用 React、Solidity 和 Web3.js 构建真实世界的 dApp。

项目地址:github.com/QuintionTan…

工具概览

下面将介绍在这篇文章中即将使用的一些工具。

  • 智能合约工具:Truffle Framework,提供了一套用于开发以太坊智能合约的工具。它提供了智能合约管理、部署和迁移、网络管理、开发控制台等工具。
  • 以太坊测试链:Ganache,一个个人区块链,一个本地开发区块链,可以用来模仿公共区块链的所有行为。
  • 编写智能合约语言:Solidity,一种用于实现智能合约的面向对象的高级语言。
  • 区块链钱包插件:MetaMask,本文将使用 Metamask 谷歌扩展程序,它实现了浏览器与测试以太坊区块链网络进行连接。
  • 前端开发框架:React
  • web3js:一个 JavaScript 库,允许与以太坊区块链进行通信。它将 React 应用程序变成支持区块链的应用程序。

环境准备

首先,下载按照 Truffle Framework ,使用以下命令:

npm install -g truffle

接下来,下载并安装 Ganache,安装成功打开将可以快速构建一个本地ETH网络,界面如下:

image.png

npm install ganache --global

安装后,执行以下命名可以启动一个本地 ETH 网络:

ganache

启动网络,生成一些可用于测试的账号地址和私钥,如下:

Available Accounts
==================
(0) 0xA57F1010d3BC4Acf6572c42B794B67dAf35B84f2 (1000 ETH)
(1) 0x60A73B46Ea815160c83CC3F89D855a4B8b6f21b5 (1000 ETH)
(2) 0x518B8c54ff5CA623707FB9Ca78B738425972EDdE (1000 ETH)
(3) 0x555024474B387842B8089489658d62c2Fc2D012F (1000 ETH)
(4) 0x302C59aE6F9456d74afB3Ca965b5f2123Fa58fbB (1000 ETH)
(5) 0xE0016171057C950A0E101f5affdAF01e8F5afd1f (1000 ETH)
(6) 0xC7cC6407DA22b630BEE2a8a3c304DA635eC4d9CD (1000 ETH)
(7) 0x06DCa7D808748F663D22BacC85555140D98Ee7fC (1000 ETH)
(8) 0xb1A3749c08b8440D3a45612A1d8864826d68549c (1000 ETH)
(9) 0xcF9E30D63a4970e938328470f7756c372afC8bBE (1000 ETH)
Private Keys
==================
(0) 0xcfac544a9947d80e3b37b00efad50a7ea3efab6ca51dd0808cad58f4ad9ebaae
(1) 0xecd3ae8ea7b36b7acc522ca790ae0660986f0615d73704bb4ceb03138cf9144a
(2) 0x7fa1926a2b8b2d98cc54bc9c9baf99035f7e5db1f23938a0b37a1ad8d106ce57
(3) 0xa313ceb995434887f5335813c4df01ab631b72de179044a04547e3c1142a8b46
(4) 0x2d2b70124764d8e4dd6b78a4f2cb689c27f1d42d89d2d416d9e5ad0797455fab
(5) 0x2ecdffb4009281b434f7c2e0bcc94751fb45c8667f7201ec3566e0ef79c6d82b
(6) 0x0b6ea61e43c6963cff2a4f5d0806bbd08cd99abcf5a61f92cadbecbf1bca4da3
(7) 0xfe15c45eca17dd6d67d4c044e7d4ea2740d729b76085de163143bcd19699fd9e
(8) 0xb01f9a7059c685e843339839e3398a556a74d890817cde4c09ed61828c2c08ae
(9) 0x952c883b05da76f58e30ce7fa4ed98cf8479313a1c1667c0c2d2aa10c66fea31

接下来,需要安装使用 Metamask。将 Metamask 扩展添加到 google chrome ,导入上面的账号,效果如下:

image.png

后续的开发过程将不会使用 ganache  终端来构建开发区块链网络,而是使用其客户端。

开始构建

dApp 前期准备工作已经设置好,接下来编写智能合约。打开一个终端并使用以下命令在项目文件夹中创建一个文件夹:

mkdir blockchain

现在使用以下命令在区块链文件夹中再创建一个合约文件夹:

cd blockchain
mkdir contracts
cd contracts

运行以下命令来创建一个 truffle 项目,这样就可以开发智能合约:

truffle init

使用上面的命令,应该得到如下输出:

Init successful, sweet!
Try our scaffold commands to get started:
  $ truffle create contract YourContractName # scaffold a contract
  $ truffle create test YourTestName         # scaffold a test
http://trufflesuite.com/docs

打开项目文件夹,可以看到以下文件夹和文件结构:

image.png

  • contracts 文件夹中,将编写智能合约。
  • migrations 文件夹中,将迁移新创建的智能合约。
  • test 文件夹中,通常用于编写测试用例来测试智能合约。
  • truffle-config.js 文件包含 truffle 项目的所有配置。

现在编写智能合约,在 contracts 文件夹中创建一个新文件并将其命名为 contacts.sol,并在其中写入以下代码:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

这始终是智能合约文件中的第一行,用来指定 solidity 的版本。现在来编写代码制作第一个智能合约 Contacts

contract Contacts {
}

现在定义一个状态变量来跟踪智能合约中联系人的数量,代码如下:

pragma solidity >=0.4.22 <0.9.0;
contract Contacts {
    uint public count = 0; // 状态变量
}

这是一个特殊的变量,写入该变量的任何数据都将存储在区块链存储中。代码中使用一个特殊的修饰符关键字,即 public ,定义的变量允许智能合约外对其进行访问,初始赋值为 0 。现在有了一个状态变量,并且里面有一个值。现在进入前端部分,首先尝试访问这个公共状态变量。

blockchain 目录中运行以下命令以创建一个响应应用程序:

npx create-react-app contacts

接下来进入项目目录,增加依赖:

cd contacts
yarn add web3

如果使用的是 create-react-app 版本 >=5,可能会遇到构建问题,这是因为最新版本的 create-react-app 中不包含 NodeJSpolyfill。需要按照下面的方式来解决这个问题:

yarn add --dev react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer

然后在项目根目录下创建文件 config-overrides.js ,增加以下代码:

const webpack = require("webpack");
module.exports = function override(config) {
    const fallback = config.resolve.fallback || {};
    Object.assign(fallback, {
        crypto: require.resolve("crypto-browserify"),
        stream: require.resolve("stream-browserify"),
        assert: require.resolve("assert"),
        http: require.resolve("stream-http"),
        https: require.resolve("https-browserify"),
        os: require.resolve("os-browserify"),
        url: require.resolve("url"),
    });
    config.resolve.fallback = fallback;
    config.plugins = (config.plugins || []).concat([
        new webpack.ProvidePlugin({
            process: "process/browser",
            Buffer: ["buffer", "Buffer"],
        }),
    ]);
    return config;
};

最后修改 package.json 的启动方式:

"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
}

如上所见,添加 web3.js 作为该项目的依赖项,并解决上面的问题后,以使创建的 React 应用程序与上面创建的智能合约进行交互。

将在 App.js 文件中编写所有代码,代码如下:

import { useEffect, useState } from "react";
import Web3 from "web3";
function App() {
    const [account, setAccount] = useState(); // 设置账号的状态变量
    useEffect(() => {
        async function load() {
            const web3 = new Web3(
                Web3.givenProvider || "http://localhost:7545"
            );
            const accounts = await web3.eth.requestAccounts();
            setAccount(accounts[0]);
        }
        load();
    }, []);
    return <div>当前账号: {account}</div>;
}
export default App;

为了能够成功在本地运行应用,需要在后端上调整一些配置,回到合同文件夹,打开 truffle-config.js 文件并添加以下属性:

module.exports = {
    networks: {
        development: {
            host: "127.0.0.1",
            port: 7545,
            network_id: "*",
        },
    },
    compilers: {
        solc: {
            optimizer: {
                enabled: true,
                runs: 200,
            },
        },
    },
};

然后在 migrations 文件夹中,创建一个新文件并将其命名为 2_deploy_contracts.js 代码如下:

const Contacts = artifacts.require("./Contacts.sol");
module.exports = function (deployer) {
    deployer.deploy(Contacts);
};

在终端中,输入一下命令以迁移智能合约:

cd contracts
truffle migrate

应该会看到如下输出:

image.png

回到前端 React 文件夹并运行以下命令来运行应用程序:

cd contacts
yarn start

这将在浏览器中打开 React 应用程序,并会触发 Metamask 与区块链网络进行交互,应该会看到如下所示的屏幕:

image.png

现在已经成功地与智能合约交互并检索了账户 ID,继续在智能合约中创建一个新函数来获取联系人列表并将其发送到前端并在视图上呈现它们。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
contract Contacts {
    uint public count = 0; // 状态变量
    struct Contact {
        uint id;
        string name;
        string phone;
    }
    constructor() public {
        createContact('QuintionTang', '18900000000');
    }
    mapping(uint => Contact) public contacts;
    function createContact(string memory _name, string memory _phone) public {
        count++;
        contacts[count] = Contact(count, _name, _phone);
    }
}

现在再次迁移此合约:

truffle migrate

完成后回到前端React 应用程序,在 src/ 文件夹中创建一个新文件 config.js,配置合约地址和合约ABI,这些信息可以在文件blockchain/contracts/build/contracts/Contacts.json 中找到 ,代码如下:

export const CONTRACT_ADDRESS = "0xD3329a764935C7AA9B32f162C196E28B4e3310A3"; // 合约地址
export const CONTRACT_ABI = [
    {
        inputs: [],
        payable: false,
        stateMutability: "nonpayable",
        type: "constructor",
    },
    {
        constant: true,
        inputs: [
            {
                internalType: "uint256",
                name: "",
                type: "uint256",
            },
        ],
        name: "contacts",
        outputs: [
            {
                internalType: "uint256",
                name: "id",
                type: "uint256",
            },
            {
                internalType: "string",
                name: "name",
                type: "string",
            },
            {
                internalType: "string",
                name: "phone",
                type: "string",
            },
        ],
        payable: false,
        stateMutability: "view",
        type: "function",
    },
    {
        constant: true,
        inputs: [],
        name: "count",
        outputs: [
            {
                internalType: "uint256",
                name: "",
                type: "uint256",
            },
        ],
        payable: false,
        stateMutability: "view",
        type: "function",
    },
    {
        constant: false,
        inputs: [
            {
                internalType: "string",
                name: "_name",
                type: "string",
            },
            {
                internalType: "string",
                name: "_phone",
                type: "string",
            },
        ],
        name: "createContact",
        outputs: [],
        payable: false,
        stateMutability: "nonpayable",
        type: "function",
    },
];

现在将智能合约地址和 ABI 导入 App.js 文件,如下所示,并使用以下代码更新加载函数:

import { useEffect, useState } from "react";
import Web3 from "web3";
import { CONTRACT_ABI, CONTRACT_ADDRESS } from "./config";
function App() {
    const [account, setAccount] = useState();
    const [contactList, setContactList] = useState();
    const [contacts, setContacts] = useState([]);
    useEffect(() => {
        async function load() {
            const web3 = new Web3(
                Web3.givenProvider || "http://localhost:7545"
            );
            const accounts = await web3.eth.requestAccounts();
            setAccount(accounts[0]);
            // 使用ABI和地址实例化智能合约。
            const contactListByContract = new web3.eth.Contract(
                CONTRACT_ABI,
                CONTRACT_ADDRESS
            );
            // 将联系人列表设置为状态变量。
            setContactList(contactListByContract);
            // 得到迭代的联系人总数
            const counter = await contactListByContract.methods.count().call();
            // 遍历计数器的时间量
            for (var i = 1; i <= counter; i++) {
                // 调用 contacts 方法从智能合约中获取特定的联系人
                const contact = await contactListByContract.methods
                    .contacts(i)
                    .call();
                // 添加最近获取的联系状态变量。
                setContacts((contacts) => [...contacts, contact]);
            }
        }
        load();
    }, []);
    return (
        <div>
            当前账号: {account}
            <h1>联系人列表</h1>
            <ul>
                {Object.keys(contacts).map((contact, index) => (
                    <li key={`${contacts[index].name}-${index}`}>
                        <h4>{contacts[index].name}</h4>
                        <span>
                            <b>手机号码: </b>
                            {contacts[index].phone}
                        </span>
                    </li>
                ))}
            </ul>
        </div>
    );
}
export default App;

启动 React 应用程序可以看到如下效果:

image.png

到此,一个简单的 dAPP 已经实现了。


相关文章
|
17天前
|
前端开发 搜索推荐 JavaScript
如何通过DIY.JS快速构建出一个DIY手机壳、T恤的应用?
DIY.JS 是一款基于原生 Canvas 的业务级图形库,专注于商品定制的图形交互功能,帮助开发者轻松实现个性化设计。适用于 T 恤、手机壳等多种商品场景。它自带丰富功能,无需从零构建,快速集成到项目中。通过创建舞台、添加模型、定义 DIY 区域和添加素材四个步骤即可完成基础用法。支持在线演示体验,文档详细,易上手。
|
28天前
|
前端开发 JavaScript NoSQL
使用 Node.js、Express 和 React 构建强大的 API
本文详细介绍如何使用 Node.js、Express 和 React 构建强大且动态的 API。从开发环境搭建到集成 React 前端,再到利用 APIPost 高效测试 API,适合各水平开发者。内容涵盖 Node.js 运行时、Express 框架与 React 库的基础知识及协同工作方式,还涉及数据库连接和前后端数据交互。通过实际代码示例,助你快速上手并优化应用性能。
|
2月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
2月前
|
数据可视化 图形学 UED
从模型托管到交互开发:DataV 如何简化三维 Web 应用构建?
从模型托管到交互开发:DataV 如何简化三维 Web 应用构建?
|
2月前
|
JavaScript 前端开发 Java
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
柯里化是一种强大的函数式编程技术,它通过将函数分解为单参数形式,实现了灵活性与可复用性的统一。无论是参数复用、延迟执行,还是函数组合,柯里化都为现代编程提供了极大的便利。 从 Redux 的选择器优化到复杂的数据流处理,再到深度嵌套的函数优化,柯里化在实际开发中展现出了非凡的价值。如果你希望编写更简洁、更优雅的代码,柯里化无疑是一个值得深入学习和实践的工具。从简单的实现到复杂的应用,希望这篇博客能为你揭开柯里化的奥秘,助力你的开发之旅! 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一
|
3月前
|
安全 Linux 开发工具
零基础构建开源项目OpenIM桌面应用和pc web- Electron篇
OpenIM 为开发者提供开源即时通讯 SDK,作为 Twilio、Sendbird 等云服务的替代方案。借助 OpenIM,开发者可以构建安全可靠的即时通讯应用,如 WeChat、Zoom、Slack 等。 本仓库基于开源版 OpenIM SDK 开发,提供了一款基于 Electron 的即时通讯应用。您可以使用此应用程序作为 OpenIM SDK 的参考实现。本项目同时引用了 @openim/electron-client-sdk 和 @openim/wasm-client-sdk,分别为 Electron 版本和 Web 版本的 SDK,可以同时构建 PC Web 程序和桌面应用(Wi
166 2
|
4月前
|
中间件 API
Next.js 实战 (八):使用 Lodash 打包构建产生的“坑”?
这篇文章介绍了作者在使用Nextjs15进行项目开发时遇到的部署问题。在部署过程中,作者遇到了打包构建时的一系列报错,报错内容涉及动态代码评估在Edge运行时不被允许等问题。经过一天的尝试和调整,作者最终删除了lodash-es库,并将radash的部分源码复制到本地,解决了打包报错的问题。文章最后提供了项目的线上预览地址,并欢迎读者留言讨论更好的解决方案。
106 10
|
6月前
|
JSON 缓存 JavaScript
深入浅出:使用Node.js构建RESTful API
在这个数字时代,API已成为软件开发的基石之一。本文旨在引导初学者通过Node.js和Express框架快速搭建一个功能完备的RESTful API。我们将从零开始,逐步深入,不仅涉及代码编写,还包括设计原则、最佳实践及调试技巧。无论你是初探后端开发,还是希望扩展你的技术栈,这篇文章都将是你的理想指南。
|
5月前
|
安全 应用服务中间件 网络安全
实战经验分享:利用免费SSL证书构建安全可靠的Web应用
本文分享了利用免费SSL证书构建安全Web应用的实战经验,涵盖选择合适的证书颁发机构、申请与获取证书、配置Web服务器、优化安全性及实际案例。帮助开发者提升应用安全性,增强用户信任。
|
5月前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
111 12

热门文章

最新文章