DApp开发入门

DApp开发入门

创建Dapp的应用之前,我们使用是vite。首先我们创建一个简单的前端应用:

npx create vite

随后我们使用pnpm安装一下。

pnpm install

启动它:

pnpm dev

项目正常启动:

接着导入操作web3的库,我们这里选择ethers这个库,因为它是JavaScript中操作Web3最方便的库,安装:

pnpm install ethers

ok,我们清除掉脚手架里面代码。导入漂亮的UI库:antd。

pnpm install antd

写一个连接小狐狸的按钮:

const ConnectButton = ({ onConnect }) => { return ( <Button onClick={async () => { if (window.ethereum) { const requestAccounts = await window.ethereum.request({ method: “eth_requestAccounts”, }); if (requestAccounts) { const provider = new ethers.providers.Web3Provider(window.ethereum); const signer = provider.getSigner(); React.$provider = provider; React.$signer = signer; onConnect(true); } } else { onConnect(false); } }} > 连接小狐狸 </Button> ); }; function App(){ const [isConnect, setIsConnect] = useState(false); return ( <div className=”App” style={{ margin: “20px” }}> <ConnectButton onConnect={(connect) => { setIsConnect(connect) }} ></ConnectButton> </div> ) }

点击按钮就可以看到这个界面,然后授权即可。

我们授权了小狐狸之后,我们要获取自己的账户信息。

例如:地址,余额。

我们写一个组件来获取该用户的信息:

const RenderAccountInfo = () => { const [accountInfo, setAccountInfo] = useState({ account: “”, balance: “0” }); const [isLoading, setLoading] = useState(false); useEffect(() => { const getAccountInfo = async () => { const [account] = await React.$provider.listAccounts(); if (account) { const balanceBigInt = await React.$provider.getBalance(account); const balance = ethers.utils.formatEther(balanceBigInt); setAccountInfo({ account, balance }); setLoading(true); } }; getAccountInfo(); () => { setLoading(false); }; }, [accountInfo, isLoading]); return ( <div> {isLoading && ( <div> {accountInfo.account} – {accountInfo.balance} $ </div> )} </div> ); };

接着App这个函数要改造一下。如果连接了小狐狸的话,就不显示按钮,接着显示该信息:

function App() { const [isConnect, setIsConnect] = useState(false); useEffect(() => { if (React.$provider) { setIsConnect(true); } else { setIsConnect(false); } }, [React.$provider]); return ( <div className=”App” style={{ margin: “20px” }}> {!isConnect ? ( <ConnectButton onConnect={(connect) => { setIsConnect(connect); }} ></ConnectButton> ) : ( <div> <RenderAccountInfo></RenderAccountInfo> </div> )} </div> ); }

可能你注意到了ethers.utils.formatEther这个函数, 它是专门转换wei的工具,详情见:https://docs.ethers.io/v5/api/utils/display-logic/#utils-formatEther

部署智能合约

这里的智能合约有点协议的味道,在现实世界中,为了保障可信用化的交易,法律中提出一种合同的有效范本,那么这个范本中签约的就有了一些法律的效应。

智能合约也通用与这种范式,是一种N方共同确认的一种合约。

直接使用truffle创建一个合约:

truffle init

它会在本地创建几个文件夹,分别是:

contracts – 你编写的合约migrations – 迁移到链上的脚本truffle-config.js – truffle的配置文件

然后在contracts这个目录里面添加一份合约:

// SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; contract TodoList { string content = “”; event observerContentChange(string content); function getContent() public view returns (string memory) { return content; } function setContent(string memory _content) public { emit observerContentChange(content); content = _content; } }

注意: pragma是必填的一个关键字,它决定了这个合约的版本,而且合约的版本并不相同。

之后就是部署这个合约了,不过部署合约之前,需要写一个部署脚本进行部署的。

在migrations这个文件夹下新建: deploy_todo.js这个脚本。

脚本的内容如下:

const TodoList = artifacts.require(“TodoList”); // 合约的名字 module.exports = function (deployer) { deployer.deploy(TodoList); };

命令行直接迁移到测试链上即可:

truffle migrate

输出的命令是这样子的:

Compiling your contracts… =========================== > Compiling .\\contracts\\Migrations.sol > Compiling .\\contracts\\Todo.sol > Artifacts written to E:\\dapp\\eth-test\\build\\contracts > Compiled successfully using: – solc: 0.8.10+commit.fc410830.Emscripten.clang Starting migrations… ====================== > Network name: ganache > Network id: 5777 > Block gas limit: 6721975 (0x6691b7) 1_initial_migration.js ====================== Replacing Migrations ———————- > transaction hash: 0x250e6ea76bfcc52728fede373b3da7e192d8d5579aa5f55f6852753eade0f089 > Blocks: 0 Seconds: 0 > contract address: 0xeBa738136e4867d4749Cf15b330D685bC483269A > block number: 151 > block timestamp: 1641635104 > account: 0xDbD1794A6bdBA76352BB2C1718931B6D09c0Db3E > balance: 89.71604119999978423 > gas used: 248854 (0x3cc16) > gas price: 20 gwei > value sent: 0 ETH > total cost: 0.00497708 ETH > Saving migration to chain. > Saving artifacts ————————————- > Total cost: 0.00497708 ETH 2_deploy_todo.js ================ Replacing TodoList ——————– > transaction hash: 0x8f0f959ea236ecf72b8834e352e6928de4602c0401bba7d1431b66dc9ac3c6f5 > Blocks: 0 Seconds: 0 > contract address: 0x826153d0952cf31498736D8133Be8F83f2Da9FCD > block number: 153 > block timestamp: 1641635105 > account: 0xDbD1794A6bdBA76352BB2C1718931B6D09c0Db3E > balance: 89.70899371999978423 > gas used: 309861 (0x4ba65) > gas price: 20 gwei > value sent: 0 ETH > total cost: 0.00619722 ETH > Saving migration to chain. > Saving artifacts ————————————- > Total cost: 0.00619722 ETH Summary ======= > Total deployments: 2 > Final cost: 0.0111743 ETH

接着我们使用ganache这个软件导入truffle,就能自动生成账户等信息。

网址是:https://trufflesuite.com/ganache/

下载好了之后,新建一个工作站:

然后点击:

选择truffle.json即可:

我们就能看到账号:

ok,接着我们投入到前端的编写中。

因为我们使用ganache这个本地测试链用的方式开发,那么我们的网页也需要做出相应的改变。将连接的provider改成:

const provider = new ethers.providers.JsonRpcProvider(“<HTTP://127.0.0.1:7545>”);

然后赋值一个全局React变量:

const signer = provider.getSigner(); React.$provider = provider; React.$signer = signer;

接着载入我们写好的合约,不过首先要找到net id找到这个合约的地址,还是封装一个按钮。

把合约的abi加入进去一下即可。

import TodoList from “./../build/contracts/TodoList.json” const LoadContractButton = ()=>{ return ( <div> <Button onClick={async () => { try { const { chainId } = await React.$provider.getNetwork() const { address } = TodoList.networks[chainId]; const TodoContract = new ethers.Contract( address, TodoList.abi, React.$provider ); React.$TodoContract = TodoContract; onLoad(true); } catch (e) { onLoad(false); } }} > 载入合约 </Button> </div> ); }

我们看到了一个简单的载入按钮:

接着将App这个函数修改一下,让它在载入了合约之后。能显示合约中某一个值,我们使用一个组件来显示。

具体的写法可以参考这样:

const ContractContent = () => { const [content, setContent] = useState(“”); const getContentByContract = async () => { const content = await React.$TodoContract.getContent(); setContent(content); }; const observerContentChangeEvent = () => { }; const changeContent = async () => { const contractSigner = React.$TodoContract.connect(React.$signer); await contractSigner.setContent(Math.random().toFixed(2).toString()); }; useEffect( function () { getContentByContract(); observerContentChangeEvent(); }, [content] ); return ( <div> <div>{content}</div> <Button onClick={() => { changeContent(); }} > 修改值 </Button> </div> ); };

我们在contract里面写了一个事件,可以实时检测某一个函数调用的回调函数,有些像WebStock。

其中的这个回调函数就是监听event的啦。

React.$TodoContract.on(“observerContentChange”, (content) => { console.log(“-> changed content”, content); });

特别注意的是,修改合约时需要带着该账户的签名。

具体代码的表现是:

const contractSigner = React.$TodoContract.connect(React.$signer);

这个合约的签名就搞定了!

将这两个组件放在App函数里面,就能渲染出来内容。

点击修改值,就能看到数字改变了。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注