如何使用 React、Solidity 和 CometChat 构建一个很棒的 Web3 众筹平台
您将要构建的内容,请参见 Rinkeby 测试网络上的demo和git repo此处...
简介
Web3 开发中的一项宝贵技能是确保您在当今科技世界中获得相关机会所需要的一切。
Web3 经济正在蓬勃发展,您现在能为自己做的最好的事情就是学习这门手艺。
你如何为你的 Dapp 开发智能合约?你如何最好地设计 Dapp 界面?您如何将前端连接到位于区块链上的后端?所有这些问题都将在本教程中得到解决。
订阅我的 YouTube 频道学习如何从头开始构建 Web3 应用程序。
我还为想要从导师那里一对一学习的认真的人提供私人和专业课程。在这里预订您的 Web3 课程。
话虽如此,让我们进入教程。
先决条件
您需要安装以下工具才能与我一起构建:
-
NodeJs (超级重要)
-
EthersJs
-
硬
-
反应
-
肆虐
-
顺风 CSS
-
CometChat SDK
-
元掩码
-
Yarn ## 安装依赖
从下面的存储库中克隆项目并运行以下命令。
git clone https://github.com/Daltonic/genesis <PROJECT_NAME>
cd <PROJECT_NAME>
yarn install
进入全屏模式 退出全屏模式
执行以下代码会将以下依赖项和版本添加到您的项目中。
配置 CometChat SDK
按照以下步骤配置CometChat SDK;最后,您必须将这些键保存为环境变量。
步骤1:
前往CometChat仪表板并创建一个帐户。
第2步:
登录CometChat仪表板,仅在注册后。
第 3 步:
在仪表板中,添加一个名为 genesis. 的新应用程序。
第4步:
从列表中选择您刚刚创建的应用程序。
第 5 步:
从 Quick Start 将APP_ID
、REGION
和AUTH_KEY
复制到您的.env
文件中。请参阅图像和代码片段。
将REACT_COMET_CHAT
占位符键替换为相应的值。
REACT_APP_COMET_CHAT_REGION=**
REACT_APP_COMET_CHAT_APP_ID=**************
REACT_APP_COMET_CHAT_AUTH_KEY=******************************
进入全屏模式 退出全屏模式
**.env**
文件应在项目的根目录中创建。
配置 Infuria 应用程序
步骤1:
前往Infuria,创建一个帐户。
第2步:
从仪表板创建一个新项目。
第 3 步:
将Rinkeby
测试网络 WebSocket 或 HTTPs 端点 URL 复制到您的.env
文件。
之后,输入您的 Metamask 密码短语和首选帐户的私钥。如果您正确地按照说明进行操作,您的环境变量现在应该如下所示。
ENDPOINT_URL=***************************
DEPLOYER_KEY=**********************
REACT_APP_COMET_CHAT_REGION=**
REACT_APP_COMET_CHAT_APP_ID=**************
REACT_APP_COMET_CHAT_AUTH_KEY=******************************
进入全屏模式 退出全屏模式
如果您不知道如何访问您的私钥,请参阅下面的部分。
访问您的 Metamask 私钥
步骤1:
确保在您的 Metamask 浏览器扩展中选择 Rinkeby 作为测试网络。然后,在首选帐户上,单击垂直虚线并选择帐户详细信息。请看下图。
第2步:
在提供的字段中输入您的密码,然后单击确认按钮,这将使您能够访问您的帐户私钥。
第 3 步:
单击**“导出私钥**”以查看您的私钥。确保您永远不会在公共页面(例如Github
)上公开您的密钥。这就是为什么我们将其附加为环境变量。
第4步:
将您的私钥复制到您的.env
文件中。请参阅下面的图像和代码片段:
ENDPOINT_URL=***************************
SECRET_KEY=******************
DEPLOYER_KEY=**********************
REACT_APP_COMET_CHAT_REGION=**
REACT_APP_COMET_CHAT_APP_ID=**************
REACT_APP_COMET_CHAT_AUTH_KEY=******************************
进入全屏模式 退出全屏模式
至于你的SECRET_KEY
,你需要把你的**Metamask**
密语粘贴到环境文件提供的空间里。
创世纪智能合约
现在让我们为这个项目创建智能合约。在此之前,您需要了解该项目的范围。
我们正在创建一个众筹平台,初创公司和优秀的项目可以在这里筹集资金。这个平台帮助创始人有资本创业,叫做Genesis,意思是BEGINING!
在项目的 src 目录中创建一个名为 contracts 的文件夹。现在,前往 src >> contracts 并创建一个名为 Genesis.sol 的文件并将下面的代码粘贴到其中。
现在让我们逐段讨论这个智能合约的内容。
定义基本变量
address public owner;
uint public projectTax;
uint public projectCount;
uint public balance;
statsStruct public stats;
projectStruct[] projects;
mapping(address => projectStruct[]) projectsOf;
mapping(uint => backerStruct[]) backersOf;
mapping(uint => bool) public projectExist;
进入全屏模式 退出全屏模式
在上面的代码块中,我们定义了用于在区块链上存储数据的各种数据类型和结构。
对于单一数据类型,我们的智能合约中有owner
变量、项目tax
、count
和可用的balance
。
对于多数据类型,我们有**statsStruct**
和**projectStruct**
,它们定义了每个项目的模型和我们智能合约中的统计数据。
映射承载项目所有者、支持者以及项目存在的记录。
如果您是 Solidity 新手,我在 YouTube 上有一个完整的免费课程,名为 Mastering Solidity Basics。所以一定要看看,喜欢,订阅!
设置基本结构和事件
enum statusEnum {
OPEN,
APPROVED,
REVERTED,
DELETED,
PAIDOUT
}
struct statsStruct {
uint totalProjects;
uint totalBacking;
uint totalDonations;
}
struct backerStruct {
address owner;
uint contribution;
uint timestamp;
bool refunded;
}
struct projectStruct {
uint id;
address owner;
string title;
string description;
string imageURL;
uint cost;
uint raised;
uint timestamp;
uint expiresAt;
uint backers;
statusEnum status;
}
event Action (
uint256 id,
string actionType,
address indexed executor,
uint256 timestamp
);
进入全屏模式 退出全屏模式
上面的代码包含用于声明枚举、结构和事件的结构。
枚举被定义为包含项目在我们平台中可以具有的各种状态。
**statsStruct**
包含一个统计数据应该包含的代码,例如捐赠、支持和项目的总数。
另一方面,**backerStruct**
包含支持项目的每个人都应该拥有的数据类型。支持者绝对应该有一个地址,他捐赠了多少,捐赠的时间和退款状态,以显示所有者是否已退还他的钱。
**projectStruct**
描述了每个项目必须包含的内容,最后,事件是根据调用函数输出信息的动态事件。
构造函数和所有者修改函数
modifier ownerOnly(){
require(msg.sender == owner, "Owner reserved only");
_;
}
constructor(uint _projectTax) {
owner = msg.sender;
projectTax = _projectTax;
}
进入全屏模式 退出全屏模式
每当将**ownerOnly()**
修饰符附加到调用函数时,它都会限制其仅对智能合约的部署者的可访问性。
另一方面,**constructor()**
函数初始化所有者状态变量以及每个项目的税。该税是每个获批项目收取的税款。
项目创建功能
function createProject(
string memory title,
string memory description,
string memory imageURL,
uint cost,
uint expiresAt
) public returns (bool) {
require(bytes(title).length > 0, "Title cannot be empty");
require(bytes(description).length > 0, "Description cannot be empty");
require(bytes(imageURL).length > 0, "ImageURL cannot be empty");
projectStruct memory project;
project.id = projectCount;
project.owner = msg.sender;
project.title = title;
project.description = description;
project.imageURL = imageURL;
project.cost = cost;
project.timestamp = block.timestamp;
project.expiresAt = expiresAt;
projects.push(project);
projectExist[projectCount] = true;
projectsOf[msg.sender].push(project);
stats.totalProjects += 1;
emit Action (
projectCount++,
"PROJECT CREATED",
msg.sender,
block.timestamp
);
return true;
}
进入全屏模式 退出全屏模式
此方法接收项目信息并将其推送到项目的数组中。接下来,它设置其他记录,例如统计数据和项目所有者。最后,发出一个事件,其中包含刚刚创建的项目的一些记录。
项目更新功能
function updateProject(
uint id,
string memory title,
string memory description,
string memory imageURL,
uint expiresAt
) public returns (bool) {
require(msg.sender == projects[id].owner, "Unauthorized Entity");
require(projectExist[id], "Project not found");
require(bytes(title).length > 0, "Title cannot be empty");
require(bytes(description).length > 0, "Description cannot be empty");
require(bytes(imageURL).length > 0, "ImageURL cannot be empty");
projects[id].title = title;
projects[id].description = description;
projects[id].imageURL = imageURL;
projects[id].expiresAt = expiresAt;
emit Action (
id,
"PROJECT UPDATED",
msg.sender,
block.timestamp
);
return true;
}
进入全屏模式 退出全屏模式
此函数使用标题、描述、图像 URL 和过期时间根据项目 ID 更新项目。完成后,它会发出一个 Project Updated 事件。
删除功能
function deleteProject(uint id) public returns (bool) {
require(projectExist[id], "Project not found");
require(projects[id].status == statusEnum.OPEN, "Project no longer opened");
require(
msg.sender == projects[id].owner ||
msg.sender == owner,
"Unauthorized Entity"
);
projects[id].status = statusEnum.DELETED;
performRefund(id);
emit Action (
id,
"PROJECT DELETED",
msg.sender,
block.timestamp
);
return true;
}
进入全屏模式 退出全屏模式
上面的函数将项目标记为已完成,通过这样做,执行退款操作,将所有捐款发回给适当的捐助者。
其他功能
这些是以下职能的职责;
-
PerformPayout():将出资释放给项目所有者,同时支付平台的任务。
-
PayOutProject():执行条件检查并在内部调用performPayout() 函数。
-
PerformRefund():将资金返还给特定项目的支持者。
-
RequestRefund():执行条件检查并调用performRefund() 函数。
-
BackProject():向特定项目捐款。
-
ChangeTax():为项目更改平台的任务。
-
GetProject():按 Id 返回特定项目详细信息。
-
GetProjects():返回智能合约上的所有项目。
-
GetBackers():返回特定项目的支持者列表。
-
PayTo():将特定金额的钱发送到特定地址。
太棒了,你已经有了智能合约,现在让我们开始将它与 React 前端合并。
配置部署脚本
与智能合约有关的另一件事是配置部署脚本。前往 scripts >> deploy.js 并将下面的代码粘贴到其中。
const hre = require('hardhat')
const fs = require('fs')
async function main() {
const taxFee = 5 // unit is in percent
const Contract = await hre.ethers.getContractFactory('Genesis')
const contract = await Contract.deploy(taxFee)
await contract.deployed()
const address = JSON.stringify({ address: contract.address }, null, 4)
fs.writeFile('./src/abis/contractAddress.json', address, 'utf8', (err) => {
if (err) {
console.error(err)
return
}
console.log('Deployed contract address', contract.address)
})
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
进入全屏模式 退出全屏模式
上面的代码在执行时编译智能合约并将其部署到指定的网络。
查看安全帽配置文件
前往项目的根目录并打开hardhat.config.js
。
require("@nomiclabs/hardhat-waffle");
require('dotenv').config()
module.exports = {
defaultNetwork: "localhost",
networks: {
hardhat: {
},
localhost: {
url: "http://127.0.0.1:8545"
},
rinkeby: {
url: process.env.ENDPOINT_URL,
accounts: [process.env.DEPLOYER_KEY]
}
},
solidity: {
version: '0.8.11',
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
paths: {
sources: "./src/contracts",
artifacts: "./src/abis"
},
mocha: {
timeout: 40000
}
}
进入全屏模式 退出全屏模式
注意称为网络和路径的两个对象。我们指定了两个网络,即 localhost 和 Rinkeby 测试网络。 localhost 用于开发目的,Rinkeby 用于制作/登台目的。
名为ENDPOINT_URL
和DEVELOPER_KEY
的环境变量分别来自 Infuria 和 Metamask。
对于路径,我们指定我们的智能合约所在的位置以及转储生成的工件的位置。对于这种情况,我们指定它们应保存在 src 文件夹中。
太好了,最后启动您的安全帽区块链服务器并使用以下命令部署智能合约。
yarn hardhat node
yarn hardhat run scripts/deploy.js --network localhost
进入全屏模式 退出全屏模式
开发前端
让我们逐步将 React 前端的各个组件放在一起。
成分
现在在 src 目录中创建一个名为 component 的文件夹。这是所有组件都将存在的地方。
标题组件
该组件精美地包含两个项目,帮助您导航回主页的徽标和帮助您连接到 Metamask 钱包的**connect wallet**
按钮。创建一个名为 Header.jsx 的文件并将下面的代码粘贴到其中。
英雄组件
该组件负责显示有关平台上创建的项目的统计信息,包括支持和捐赠的总数。
要复制,请在 components 文件夹中创建一个名为 Hero.jsx 的组件,并将下面的代码粘贴到其中。
项目和 ProjectCard 组件
该组件负责渲染项目的各个卡片。在 components 目录中,创建一个名为 Projects.jsx 的文件并将下面的代码粘贴到其中。
再次,在 components 文件夹中创建另一个名为**ProjectCard.jsx**
的文件,并将以下代码粘贴到其中。
太好了,我们正在前进。
AddButton 和 CreateProject 组件
这个组件使我们能够创建新项目。充分利用 Tailwind 的 CSS 来制作出漂亮的模态框。
在 components 文件夹中,创建一个名为**AddButton.jsx**
的组件并将下面的代码粘贴到其中。
接下来,创建另一个名为**CreateProject.jsx**
的组件,该组件被配置为由**AddButton.jsx**
组件启动。将下面的代码粘贴到其中。
ProjectDetails 组件
该组件负责显示特定项目的详细信息,并提供一些关键按钮来调用其他组件。要复制此组件,请在 components 目录中创建一个名为**ProjectDetails.jsx**
的文件,并将以下代码粘贴到其中。
更新项目组件
就像它的名字一样直观,这个组件可以帮助我们更新项目,前提是用户有正确的权限。在 components 文件夹中,创建一个名为**UpdateProject.jsx**
的文件并将下面的代码粘贴到其中。
删除项目组件
与 UpdateProject 组件一样,只有当您是项目的所有者或您是智能合约的部署者时,才能使用此组件。前往 components 文件夹并创建一个名为 DeleteProject.jsx 的新文件并将下面的代码粘贴到其中。
ChatAuth 组件
这个小组件负责对匿名聊天的用户进行身份验证。同样通过这个组件,项目所有者可以创建新项目,其他人也可以加入。
创建一个名为**ChatAuth.jsx**
的文件并将下面的代码粘贴到其中。
BackProject 组件
该组件可帮助您指定和捐赠您愿意为项目贡献的以太币数量。为了复制,让我们创建另一个名为**BackProject.jsx**
的组件并将代码粘贴到其中。
ProjectBackers 组件
该组件列出了特定项目的所有支持者。创建一个名为**ProjectBackers.jsx**
的文件并将代码粘贴到其中。
消息组件
该组件负责将聊天内容渲染到视图。首先,创建一个名为**Messages.jsx**
的文件并将以下代码粘贴到其中。
观点
我们对这个项目有三个主要视图,它们包括主页、项目和聊天页面。让我们一个接一个地创建它们。
在 src 目录中创建一个名为 views 的新文件夹,并在其中创建以下文件。
主页
该组件捆绑了构成主页的较小组件和服务。在 views 目录中创建一个名为**Home.jsx**
的文件,并将以下代码粘贴到其中。
项目视图
这个页面的制作有很多逻辑,可以从与其集成的可操作按钮的数量中看出。要实现,请在 src >> views 中创建一个名为Project.jsx
的新文件,并将以下代码粘贴到其中。
聊天视图
此视图允许您使用 CometChat SDK 进行一对多聊天。这是如何复制它,跳转到视图目录,创建一个名为**Chat.jsx**
的文件,并将下面的代码粘贴到其中。
应用组件
这个母组件捆绑和管理这个应用程序中的所有页面和组件。在 src 文件夹中,将名为**App.jsx**
的文件中的代码替换为以下代码。
这完成了这个项目的视图和组件,现在让我们添加其余的基本文件。
其他基本文件
商店服务
我们正在使用一个名为react-hooks-global-state的中等状态管理库来存储来自区块链的数据。执行此方法大大简化了代码。
在您的 src 目录中,创建一个文件夹和一个名为 store >> index.jsx 的文件,并将以下代码粘贴到其中。
区块链服务
现在,这是这个项目中最重要的文件之一,与我们的智能合约交互的所有代码都写在这里。在 src 文件夹中创建一个名为**Genesis.jsx**
的文件并粘贴以下代码。
CometChat 服务
此文件包含与 CometChat SDK 建立聊天通信所需的所有功能。在您的 src 文件夹中创建一个名为**CometChat.jsx**
的文件并将下面的代码粘贴到其中。
如果您需要我帮助解决项目中的问题,在此页面上咨询我。
Index.jsx 文件
最后一步负责初始化 CometChat 服务。在 src 文件夹中,打开**index.jsx**
并将其代码替换为下面的代码。
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import './index.css'
import App from './App'
import { initCometChat } from './CometChat'
initCometChat().then(() => {
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
)
})
进入全屏模式 退出全屏模式
你有它,你刚刚完成了整个构建。
运行以下命令以启动您的服务器。
yarn start
进入全屏模式 退出全屏模式
现在在 YouTube 上观看我的免费 web3 教程。
结论
我们已经完成了这个 Crowd Funding 构建,我知道您从与我的合作中获得了很多价值。
无论你是什么级别,如果你想在 web3 开发技能上更快地成长,进入我的私人课程。
直到下一次,继续建设!
关于作者
Gospel Darlington 是一名全栈区块链开发人员,拥有6+
年软件开发行业经验。
通过结合软件开发、写作和教学,他演示了如何在与 EVM 兼容的区块链网络上构建去中心化应用程序。
他的筹码包括JavaScript
、React
、Vue
、Angular
、Node
、React Native
、NextJs
、Solidity
等。
有关他的更多信息,请访问并关注他的页面Twitter,Github,LinkedIn, 或他的]0 网站(https://daltonic.github.io/).
更多推荐
所有评论(0)