Web3开发实战:基于luzhenqian/web3-examples的DApp构建指南

news2026/5/5 2:56:51
1. 项目概述与核心价值最近在捣鼓一些去中心化应用DApp的原型发现很多教程要么太理论化要么就是代码片段零散想找个能直接跑起来、覆盖主流场景的完整例子集还真得费一番功夫。直到我遇到了luzhenqian/web3-examples这个项目它就像是一个为开发者准备的“Web3 工具箱”里面塞满了可以直接上手、开箱即用的代码示例。这个项目不是一个单一的应用程序而是一个精心组织的代码仓库旨在通过具体的、可运行的代码来演示如何与以太坊、智能合约以及各种 Web3 基础设施进行交互。对于刚接触 Web3 开发的开发者来说最大的痛点往往不是理解概念而是不知道如何将概念落地成代码。比如你知道钱包连接很重要但 MetaMask 的 API 怎么调用你知道要读取链上数据但具体用什么库、怎么写查询你知道智能合约交互是核心但如何构造交易、处理 gas、监听事件luzhenqian/web3-examples的价值就在于它把这些分散的、关键的“操作单元”都封装成了一个个独立的、可运行的示例。你不需要从零开始搭建环境、配置依赖、摸索 API直接克隆项目按照说明运行就能看到效果然后基于这些示例进行修改和扩展效率提升非常明显。这个项目适合所有对 Web3 开发感兴趣的开发者无论你是前端工程师想给网站添加区块链功能还是后端工程师想了解如何与节点交互甚至是产品经理想快速验证一个链上交互流程的可行性都能在这里找到对应的“积木”。接下来我将带你深入拆解这个项目看看它都包含了哪些宝藏以及如何最高效地利用它来加速你的开发。2. 项目结构与核心模块解析2.1 整体目录架构与设计哲学打开luzhenqian/web3-examples的仓库你会发现它的结构非常清晰不是一个大杂烩而是按功能模块和场景进行了精心分类。这种结构本身就体现了其设计哲学模块化、场景化、最小化。每个示例都力求解决一个具体的问题依赖尽可能少代码尽可能简洁让你能快速聚焦于核心逻辑。一个典型的目录结构可能如下具体可能随项目更新而变化但思路一致web3-examples/ ├── README.md # 项目总览和快速开始指南 ├── package.json # 项目根依赖如果有的话 ├── examples/ # 核心示例目录 │ ├── 01-connect-wallet/ # 示例1连接钱包如MetaMask │ ├── 02-read-chain-data/ # 示例2读取链上数据余额、区块号 │ ├── 03-send-transaction/ # 示例3发送普通交易ETH转账 │ ├── 04-smart-contract/ # 示例4智能合约交互读写 │ │ ├── 04a-compile-deploy/ # 子示例编译与部署合约 │ │ ├── 04b-call-view/ # 子示例调用视图函数 │ │ └── 04c-send-transaction/ # 子示例发送合约交易 │ ├── 05-event-listening/ # 示例5监听合约事件 │ ├── 06-gas-optimization/ # 示例6Gas费用估算与优化 │ └── ... # 更多示例如IPFS集成、多链支持等 ├── contracts/ # 示例用的智能合约源码Solidity └── utils/ # 公共工具函数如Provider初始化、ABI处理这种结构的好处是你可以像查字典一样按需索骥。想实现钱包连接直接进入01-connect-wallet。需要和合约交互04-smart-contract下的三个子示例分别对应了部署、读、写这三个最关键的操作。每个目录通常都包含index.html一个极简的HTML前端界面展示功能。script.js或main.js核心的JavaScript逻辑代码。README.md该示例的专属说明包括运行步骤、代码讲解、注意事项。package.json该示例的独立依赖如果项目未使用Monorepo管理则可能统一在根目录。注意在实际使用中务必先阅读每个示例目录下的README.md。里面通常会明确指出需要预先安装的依赖如web3.js或ethers.js的特定版本、需要配置的节点RPC地址如Infura或Alchemy的项目ID以及是否需要运行本地测试链如Ganache。2.2 核心技术栈与工具选型这个项目示例主要基于 JavaScript/TypeScript 生态这是目前 Web3 前端和脚本开发最主流的语言。核心库的选择通常围绕两大巨头web3.js和ethers.js。项目可能会同时展示两种库的实现或者根据示例的侧重点选择其一。web3.js历史悠久功能全面是许多开发者的入门选择。它的API设计相对更“古典”与以太坊JSON-RPC接口的映射比较直接。在示例中你可能会看到如何初始化Web3实例、调用web3.eth下的各种方法。ethers.js后来居上以开发者体验、代码清晰度和安全性著称。它的API设计更现代对TypeScript支持更好并且将“Provider”提供者用于读取和“Signer”签名者用于写入的概念分离得非常清晰这有助于编写更安全的代码。很多新项目更倾向于使用ethers.js。除了核心库示例中可能还会涉及以下工具开发环境Node.jsnpm/yarn/pnpm是标配。有些示例可能需要一个简单的静态文件服务器比如使用http-server或live-server。智能合约开发对于涉及合约编译和部署的示例会用到solcSolidity编译器或更流行的开发框架如Hardhat或Truffle。这些框架封装了编译、测试、部署的全流程。本地测试链为了安全且免费地测试交易示例很可能推荐使用Ganache。它可以一键启动一个本地的以太坊测试网络并预分配一批有测试ETH的账户。节点服务连接公共测试网或主网时需要节点服务提供商。示例中常会用到Infura或Alchemy的免费层级你需要注册并获取一个项目IDProject ID或API密钥API Key并替换示例代码中的占位符。钱包前端示例自然离不开钱包。MetaMask浏览器扩展是绝对的主角示例会演示如何检测其是否安装、如何请求账户连接、如何监听账户切换等。实操心得如果你是新手我建议先从ethers.js的示例看起它的概念分离更清晰错误信息也更友好。在配置节点服务时Alchemy 的免费套餐目前通常比 Infura 提供更高的请求速率对于开发和测试更友好。另外务必在MetaMask中配置好对应的测试网络如Sepolia、Goerli并领取一些测试网ETH否则所有发送交易的操作都会失败。3. 关键示例深度拆解与实操3.1 钱包连接从检测到权限管理钱包连接是任何DApp的门户。01-connect-wallet示例看似简单但里面包含了与用户钱包安全交互的所有基础要素。我们以ethers.js和 MetaMask 为例拆解其核心代码。首先你需要检测window.ethereum对象是否存在这是 MetaMask 注入到页面中的对象。if (typeof window.ethereum ! undefined) { console.log(MetaMask is installed!); // 初始化Provider const provider new ethers.BrowserProvider(window.ethereum); } else { // 处理未安装MetaMask的情况通常引导用户去安装 alert(Please install MetaMask to use this dApp!); }接下来是核心的连接请求。这里有一个关键点provider.send(eth_requestAccounts, [])这个调用会触发 MetaMask 弹出授权窗口请求用户连接其账户。这是唯一需要用户明确授权才能获取账户地址的操作。async function connectWallet() { try { // 请求账户访问权限 const accounts await provider.send(eth_requestAccounts, []); const account accounts[0]; console.log(Connected account:, account); document.getElementById(walletAddress).innerText Connected: ${account.substring(0, 6)}...${account.substring(38)}; // 获取Signer用于后续签名交易 const signer await provider.getSigner(); window.signer signer; // 存储起来供其他函数使用 } catch (error) { console.error(User denied account access or other error:, error); } }重要注意事项不要频繁调用连接请求eth_requestAccounts会弹出窗口频繁调用会惹恼用户。通常只在用户点击“连接钱包”按钮时调用一次之后通过监听事件来获取账户变化。监听账户和链变更用户可能在 MetaMask 中切换账户或切换网络你的 DApp 需要响应这些变化。window.ethereum.on(accountsChanged, (accounts) { if (accounts.length 0) { // 用户断开了连接 console.log(Please connect to MetaMask.); } else { // 用户切换了账户更新界面 console.log(Switched to account:, accounts[0]); } }); window.ethereum.on(chainChanged, (chainId) { // 链ID发生变化如从主网切换到测试网强烈建议页面重载 window.location.reload(); });权限持久化从 MetaMask 的某个版本开始连接权限默认不是永久的。用户关闭浏览器标签页后下次访问可能需要重新连接。更优雅的做法是使用eth_accounts方法静默尝试获取已授权的账户如果失败再引导用户点击连接。3.2 智能合约交互读、写与事件监听这是 Web3 开发的核心。04-smart-contract下的示例会完整展示这一流程。假设我们有一个简单的智能合约SimpleStorage它有一个状态变量storedData和对应的set、get函数。第一步准备合约接口ABI和地址合约的 ABI应用二进制接口就像它的“说明书”定义了有哪些函数可以调用、参数是什么。地址则是合约在区块链上的“门牌号”。在示例中ABI 通常以一个 JSON 数组的形式硬编码在 JS 文件里或者从编译后的文件中导入。// 简化的合约ABI const simpleStorageABI [ function get() public view returns (uint256), function set(uint256 _value) public, event ValueChanged(address indexed author, uint256 oldValue, uint256 newValue) ]; const contractAddress 0x...; // 部署后的合约地址第二步初始化合约实例使用ethers.js你需要一个Signer或Provider来初始化合约实例。Signer用于发送交易写Provider用于调用视图函数读。// 用于读取的合约实例不需要签名 const contractRead new ethers.Contract(contractAddress, simpleStorageABI, provider); // 用于写入的合约实例需要签名 const contractWrite new ethers.Contract(contractAddress, simpleStorageABI, signer);第三步调用视图函数读操作读取链上数据是免费的不需要 gas也不需要用户签名。直接调用即可。async function getStoredData() { try { const value await contractRead.get(); console.log(Stored value:, value.toString()); document.getElementById(displayValue).innerText value.toString(); } catch (error) { console.error(Error reading from contract:, error); } }第四步发送交易调用写操作修改链上状态需要发送交易支付 gas并由用户签名。这是一个异步过程会返回一个交易响应TransactionResponse然后你需要等待交易被矿工打包交易收据 TransactionReceipt。async function setStoredData(newValue) { // 输入验证 if (!newValue || isNaN(newValue)) { alert(Please enter a valid number); return; } try { console.log(Sending transaction to set value to, newValue); // 1. 发送交易 const txResponse await contractWrite.set(newValue); console.log(Transaction sent! Hash:, txResponse.hash); // 2. 等待交易被打包1个区块确认 const txReceipt await txResponse.wait(1); console.log(Transaction confirmed in block:, txReceipt.blockNumber); alert(Value set successfully!); // 3. 更新显示 await getStoredData(); } catch (error) { // 特别处理用户拒绝签名的情况 if (error.code ACTION_REJECTED) { alert(User rejected the transaction.); } else { console.error(Error setting value:, error); alert(Failed to set value. See console for details.); } } }第五步监听合约事件智能合约可以通过事件Event向外广播状态变化。前端监听这些事件可以实现实时更新。// 监听ValueChanged事件 contractRead.on(ValueChanged, (author, oldValue, newValue, event) { console.log(New event! Author: ${author}, Value changed from ${oldValue.toString()} to ${newValue.toString()}); // 实时更新UI document.getElementById(displayValue).innerText newValue.toString(); }); // 注意在页面卸载或组件销毁时记得移除监听器以避免内存泄漏 // window.addEventListener(beforeunload, () { // contractRead.removeAllListeners(ValueChanged); // });踩坑实录Gas 估算失败在发送交易前ethers.js或web3.js会尝试估算交易所需的 gas。如果合约函数逻辑有问题例如在视图函数中错误地修改了状态或者当前调用上下文如msg.sender的权限不满足合约要求会导致估算失败从而交易无法发送。错误信息可能比较晦涩需要结合合约代码排查。交易卡住Stuck有时交易发送后长时间处于pending状态。这可能是你设置的 gas 价格太低网络拥堵时矿工不打包。解决方法一是在钱包里加速或取消该笔交易MetaMask 支持此功能二是在代码中适当提高maxPriorityFeePerGas和maxFeePerGas。事件监听不触发确保监听的是正确的合约实例用Provider初始化的那个并且事件名拼写与合约中完全一致。另外监听是从设置之后开始的历史事件不会被触发。如果需要获取历史事件需要使用queryFilter方法。3.3 Gas费用处理与优化策略Gas 是以太坊网络的“燃料费”是每个开发者都必须面对的现实问题。06-gas-optimization示例会展示如何获取和计算 Gas 费用。在 EIP-1559 之后Gas 费由两部分组成基础费Base Fee和优先费Priority Fee即小费。基础费由网络燃烧优先费给矿工。async function sendTransactionWithDynamicGas() { const tx { to: someAddress, value: ethers.parseEther(0.001), // 发送 0.001 ETH // 不再简单地指定 gasPrice而是指定 maxFeePerGas 和 maxPriorityFeePerGas }; // 获取当前网络的Gas费用建议 const feeData await provider.getFeeData(); console.log(Fee Data:, feeData); // 构建交易通常会在网络建议的基础上增加一个缓冲 tx.maxPriorityFeePerGas feeData.maxPriorityFeePerGas * 120n / 100n; // 增加20%作为小费 tx.maxFeePerGas (feeData.maxFeePerGas * 120n / 100n); // 总的最大费用也增加20% tx.gasLimit 21000n; // 对于简单的ETH转账gasLimit通常是21000 // 或者更简单的方式让 ethers.js 自动填充这些值 // const populatedTx await signer.populateTransaction(tx); // populatedTx 会自动填充 gasLimit, maxFeePerGas, maxPriorityFeePerGas try { const txResponse await signer.sendTransaction(tx); console.log(Tx hash:, txResponse.hash); } catch (error) { console.error(Send tx error:, error); } }Gas 优化技巧合理设置 Gas Limit对于合约交互不要使用默认值或过低的值。估算失败会导致交易被拒绝。使用contract.estimateGas.functionName(args)来获取相对准确的估算值然后在此基础上增加 10%-20% 作为安全缓冲。动态计算优先费不要硬编码一个 gas 价格。像上面示例一样动态获取网络建议费用并根据网络拥堵情况可以通过provider.getFeeData()返回的gasPrice或第三方API判断进行调整。在非拥堵时段可以适当降低优先费以节省成本。合约层面的优化这是根本。减少链上存储操作、使用更便宜的数据类型如uint256比uint8在某些情况下更省gas因为以太坊存储槽是256位的、合并多个操作到一个函数中都能显著降低用户的交互成本。虽然前端无法控制但作为开发者需要有这个意识。4. 进阶场景与项目集成指南4.1 多链支持与网络切换现在的区块链生态不止以太坊。用户可能持有 Polygon、Arbitrum、BNB Chain 等网络的资产。你的 DApp 需要能够识别并适配不同的网络。每个网络都有一个唯一的链IDChain ID。MetaMask 注入的window.ethereum对象提供了request方法可以请求切换网络。async function switchToPolygonNetwork() { const polygonChainId 0x89; // Polygon Mainnet 的链ID是137十六进制是0x89 try { // 请求MetaMask切换到Polygon网络 await window.ethereum.request({ method: wallet_switchEthereumChain, params: [{ chainId: polygonChainId }], }); console.log(Switched to Polygon); } catch (switchError) { // 如果错误码是4902表示MetaMask尚未添加该网络需要我们先添加它 if (switchError.code 4902) { try { await window.ethereum.request({ method: wallet_addEthereumChain, params: [{ chainId: polygonChainId, chainName: Polygon Mainnet, nativeCurrency: { name: MATIC, symbol: MATIC, decimals: 18 }, rpcUrls: [https://polygon-rpc.com/], // 公共RPC端点 blockExplorerUrls: [https://polygonscan.com/] }] }); } catch (addError) { console.error(User rejected adding network:, addError); } } else { console.error(Failed to switch network:, switchError); } } }在你的 DApp 中最佳实践是在连接钱包后立即获取当前链ID (await provider.getNetwork())。检查当前链ID是否是你的DApp支持的网络之一。如果不支持则禁用部分功能如交易按钮并清晰提示用户切换到指定网络同时提供一个“一键切换”按钮调用上述函数。4.2 与前端框架React/Vue集成luzhenqian/web3-examples中的示例大多是原生 JS便于理解原理。但在实际项目中你很可能使用 React、Vue 或 Next.js 等框架。集成模式的核心是状态管理。以 React 为例你需要将 Provider、Signer、账户地址、网络ID、合约实例等状态提升到全局例如使用 Context、Redux 或 Zustand以便在组件间共享。// 一个简单的React Context示例 import { createContext, useContext, useState, useEffect } from react; import { ethers } from ethers; const Web3Context createContext(); export function Web3Provider({ children }) { const [account, setAccount] useState(); const [provider, setProvider] useState(null); const [signer, setSigner] useState(null); const [contract, setContract] useState(null); // 初始化 useEffect(() { if (window.ethereum) { const initProvider new ethers.BrowserProvider(window.ethereum); setProvider(initProvider); // 尝试静默获取已连接的账户 initProvider.send(eth_accounts, []).then(accounts { if (accounts.length 0) { handleAccountsChanged(accounts); } }); // 监听账户变化 window.ethereum.on(accountsChanged, handleAccountsChanged); // 清理函数 return () { window.ethereum.removeListener(accountsChanged, handleAccountsChanged); }; } }, []); const handleAccountsChanged (accounts) { if (accounts.length 0) { setAccount(); setSigner(null); } else { setAccount(accounts[0]); provider.getSigner().then(s { setSigner(s); // 初始化合约实例 const contractInstance new ethers.Contract(CONTRACT_ADDRESS, ABI, s); setContract(contractInstance); }); } }; const connectWallet async () { if (!provider) return; try { const accounts await provider.send(eth_requestAccounts, []); handleAccountsChanged(accounts); } catch (error) { console.error(error); } }; const value { account, provider, signer, contract, connectWallet }; return Web3Context.Provider value{value}{children}/Web3Context.Provider; } export const useWeb3 () useContext(Web3Context);然后在你的组件中就可以方便地调用function MyComponent() { const { account, connectWallet, contract } useWeb3(); const handleClick async () { if (!account) { await connectWallet(); } else if (contract) { await contract.someFunction(); } }; return ( button onClick{handleClick} {account ? 交互 (${account.slice(0,6)}...) : 连接钱包} /button ); }4.3 安全最佳实践与常见陷阱Web3 开发涉及真金白银安全性至关重要。以下是一些必须牢记的实践和陷阱永远不要硬编码私钥或助记词前端代码是公开的任何秘密放在前端都等于公之于众。私钥、助记词、节点服务的API密钥如果有限制IP则稍好都必须放在后端环境变量中。前端只应操作由用户钱包如MetaMask管理的账户。验证合约地址和ABI确保你交互的合约地址是正确的最好通过官方渠道如项目官网、GitHub获取。ABI也应来自可信的源码编译结果防止被篡改的ABI导致意外的函数调用。处理用户拒绝交易用户有绝对的权利拒绝签名或交易。你的代码必须优雅地处理ACTION_REJECTED或4001错误码不要将其视为程序错误而是正常的用户交互流程。防范重放攻击如果你的DApp涉及签名消息例如用于登录确保签名的消息包含chainId和nonce并且只在当前链和当前会话有效。数值精度处理区块链上通常使用最小单位如 wei 对于 ETH。前端展示时需要转换单位 (ethers.formatEther)用户输入后也需要转换回最小单位 (ethers.parseEther)。处理浮点数时要格外小心建议所有计算都在最小单位整数层面进行最后再格式化展示。RPC节点可靠性公共的免费RPC节点可能有速率限制或稳定性问题。对于生产环境建议使用可靠的付费节点服务如Alchemy, Infura付费套餐或者自建节点。前端代码中可以设置多个RPC端点作为后备。前端依赖安全定期更新web3.js、ethers.js等库到稳定版本以获取安全补丁和新功能。使用npm audit或类似工具检查依赖漏洞。5. 从示例到实战构建一个迷你DApp让我们综合运用以上知识构想一个简单的“链上留言板”DApp。这个DApp允许用户连接钱包支付少量费用比如 0.001 ETH来发布一条留言并且所有人都可以查看所有历史留言。1. 智能合约Solidity:// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract MessageBoard { struct Message { address sender; string content; uint256 timestamp; uint256 feePaid; } Message[] public messages; uint256 public fee 0.001 ether; // 发布留言的费用 event NewMessage(address indexed sender, uint256 messageId, string content, uint256 feePaid); function postMessage(string memory _content) public payable { require(msg.value fee, Incorrect fee amount); messages.push(Message(msg.sender, _content, block.timestamp, msg.value)); emit NewMessage(msg.sender, messages.length - 1, _content, msg.value); } function getAllMessages() public view returns (Message[] memory) { return messages; } function getMessageCount() public view returns (uint256) { return messages.length; } }2. 前端核心逻辑集成到React组件中:import { useState, useEffect } from react; import { ethers } from ethers; import contractABI from ./MessageBoard.json; // 编译后的ABI const CONTRACT_ADDRESS 0x...; // 部署后的地址 function MessageBoardApp() { const [account, setAccount] useState(); const [provider, setProvider] useState(null); const [contract, setContract] useState(null); const [messages, setMessages] useState([]); const [newMessage, setNewMessage] useState(); const [loading, setLoading] useState(false); useEffect(() { initWeb3(); }, []); const initWeb3 async () { if (window.ethereum) { const prov new ethers.BrowserProvider(window.ethereum); setProvider(prov); // 监听账户变化 window.ethereum.on(accountsChanged, (accs) setAccount(accs[0] || )); // 尝试获取已连接账户 const accs await prov.send(eth_accounts, []); if (accs.length 0) { setAccount(accs[0]); const signer await prov.getSigner(); const cntr new ethers.Contract(CONTRACT_ADDRESS, contractABI, signer); setContract(cntr); loadMessages(cntr); } } }; const connectWallet async () { const accs await provider.send(eth_requestAccounts, []); setAccount(accs[0]); const signer await provider.getSigner(); const cntr new ethers.Contract(CONTRACT_ADDRESS, contractABI, signer); setContract(cntr); loadMessages(cntr); }; const loadMessages async (cntr) { const count await cntr.getMessageCount(); const messagePromises []; for (let i 0; i count; i) { messagePromises.push(cntr.messages(i)); } const msgs await Promise.all(messagePromises); setMessages(msgs.map(m ({ sender: m.sender, content: m.content, timestamp: new Date(Number(m.timestamp) * 1000).toLocaleString(), feePaid: ethers.formatEther(m.feePaid) }))); }; const handleSubmit async () { if (!newMessage.trim() || !contract) return; setLoading(true); try { const fee await contract.fee(); const tx await contract.postMessage(newMessage, { value: fee }); await tx.wait(); alert(Message posted!); setNewMessage(); await loadMessages(contract); } catch (error) { console.error(error); if (error.code ACTION_REJECTED) { alert(Transaction was rejected.); } } finally { setLoading(false); } }; return ( div {!account ? ( button onClick{connectWallet}Connect Wallet/button ) : ( div pConnected: {account.slice(0,6)}...{account.slice(-4)}/p div textarea value{newMessage} onChange{(e) setNewMessage(e.target.value)} / button onClick{handleSubmit} disabled{loading} {loading ? Posting... : Post (Fee: 0.001 ETH)} /button /div h3Messages:/h3 ul {messages.map((msg, idx) ( li key{idx} strong{msg.sender.slice(0,6)}...{msg.sender.slice(-4)}/strong at {msg.timestamp}: {msg.content} /li ))} /ul /div )} /div ); }这个迷你DApp涵盖了钱包连接、读取合约数据、发送带价值的交易、监听账户变化等核心流程。你可以基于luzhenqian/web3-examples中的各个基础示例像搭积木一样将它们组合起来构建出更复杂的应用。6. 调试、测试与资源推荐6.1 开发调试技巧充分利用浏览器控制台console.log是你的好朋友。打印出交易对象、合约实例、返回的数据能帮你快速定位问题。使用console.dir可以展开查看复杂对象的完整结构。使用测试网绝对不要在主网上进行开发和测试。使用 Sepolia、Goerli已弃用或 Polygon Mumbai 等测试网。可以从水龙头faucet免费获取测试币。本地测试链 Ganache对于合约逻辑测试没有比 Ganache 更好的了。它能瞬间出块让你快速验证交易流程。记得在 MetaMask 中添加 Ganache 的网络通常是http://127.0.0.1:8545链ID 1337。区块浏览器无论是测试网还是主网交易发送后立即去对应的区块浏览器如 Etherscan、Polygonscan查看交易状态。它能告诉你交易是否成功、失败的原因如 out of gas、触发了哪些事件、消耗了多少 gas。合约验证在测试网或主网上部署合约后务必在区块浏览器上验证Verify你的合约源码。这样浏览器才能解析出 ABI让你能直接在浏览器上调用读函数并且用户也能查看合约代码增加透明度。6.2 单元测试与集成测试对于智能合约测试是生命线。虽然web3-examples可能不深入涉及测试但你必须知道其重要性。使用 Hardhat 或 Truffle 的测试框架它们提供了完整的测试环境可以模拟交易、检查事件、断言状态变化。// 一个简单的 Hardhat 测试示例 const { expect } require(chai); describe(MessageBoard, function () { it(Should post a message with correct fee, async function () { const MessageBoard await ethers.getContractFactory(MessageBoard); const board await MessageBoard.deploy(); await board.deployed(); const fee await board.fee(); await expect(board.postMessage(Hello, { value: fee })) .to.emit(board, NewMessage) .withArgs(owner.address, 0, Hello, fee); const messages await board.getAllMessages(); expect(messages[0].content).to.equal(Hello); }); });6.3 延伸学习资源与工具链官方文档ethers.js和web3.js的官方文档是必读的它们更新及时包含最准确的API说明。智能合约学习CryptoZombies游戏化学习Solidity、Solidity by Example官方示例都是很好的起点。开发框架深入学习和使用Hardhat或Foundry。它们现在是智能合约开发、测试和部署的事实标准工具链功能远超简单的示例脚本。安全审计在将任何涉及资产的合约部署到主网之前考虑进行专业的安全审计。同时自己也要学习常见漏洞如重入、整数溢出、权限检查缺失OpenZeppelin 的合约库和安全指南是宝贵资源。luzhenqian/web3-examples是一个绝佳的起点和参考手册但它不是终点。真正的学习在于动手实践修改这些示例尝试组合它们然后构建属于自己的、哪怕很小的 DApp。在这个过程中你会遇到无数错误但每一个错误的解决都会让你对 Web3 的理解更深一层。记住区块链开发的世界迭代很快保持好奇心持续学习才是最重要的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2583651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…