前端调用Solidity智能合约连接MetaMask小狐狸钱包,并在Alchemy测试网发布

前端调用Solidity智能合约连接MetaMask小狐狸钱包,并在Alchemy测试网发布

目录

1.DApp前端代码

2.solidity代码

3.编写部署代码

4.连接小狐狸钱包

5.启动 Webpack 开发服务器

6.部署到Alchemy测试网


在前一往篇文章基础上操作:

https://blog.ZEEKLOG.net/fyihdg/article/details/155675039https://blog.ZEEKLOG.net/fyihdg/article/details/155675039 已经搭建好环境,写一个简单的前端代码,创建一个网页界面,让用户可以通过浏览器与区块链上的 Counter 合约交互,连接用户的MetaMask钱包 ,与一个已部署在以太坊测试网上的 Counter 智能合约交互,显示当前计数值,并提供一个按钮让用户调用 count() 函数来递增它,同时通过事件监听实时更新界面。

1.DApp前端代码

  在vscode右键,新增 src目录,新建index.html,index.ts文件

index.html:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> </body> </html>

index.ts

import { ethers } from "ethers"; import { abi } from '../artifacts/contracts/Counter.sol/Counter.json'; function getEth() { // @ts-ignore const eth = window.ethereum; if (!eth) { throw new Error("No ethereum provider found"); } return eth; } async function requestAccess() { const eth = getEth(); const result = await eth.request({ method: "eth_requestAccounts" }) as string[]; return result && result.length > 0; } async function hasSigners() { const metamask = getEth(); const signers = await metamask.request({ method: "eth_accounts" }) as string[]; return signers.length > 0; } async function getContract() { // 1. 地址 // 2. 方法名 // 3. provider // 4. signer if (!await hasSigners() && !await requestAccess()) { throw new Error("No ethereum provider found"); } const provider = new ethers.BrowserProvider(getEth()); const address = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; const contract = new ethers.Contract( address, abi, await provider.getSigner(), ) const counter = document.createElement("div"); async function getCount() { counter.innerHTML = await contract.getCount(); } getCount(); const btn = document.createElement("button"); btn.innerHTML = "increment"; btn.onclick = async function () { await contract.count(); } contract.on(contract.filters.CounterInc(), async function ({ args }) { counter.innerHTML = args[0].toString() || await contract.getCount(); }) document.body.appendChild(counter); document.body.appendChild(btn); } async function main() { await getContract(); } main();

在package.json文件所在目录下新建,webpack.common.js,webpack.dev.js,webpack.prod.js,.env文件

webpack.common.js

const dotenv = require("dotenv"); dotenv.config(); const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: "./src/index.ts", // bundle"s entry point output: { path: path.resolve(__dirname, "dist"), // output directory filename: "[name].js", // name of the generated bundle }, resolve: { extensions: [".js", ".ts", ".json"], }, module: { rules: [ { test: /\.ts$/, loader: "ts-loader", exclude: /node_modules/, }, { test: /\.css$/i, use: ["style-loader", "css-loader"], }, ], }, plugins: [ new HtmlWebpackPlugin({ template: "./src/index.html", inject: "body", }), ], };

webpack.dev.js

 const webpack = require("webpack"); const { merge } = require("webpack-merge"); const baseConfig = require("./webpack.common"); module.exports = merge(baseConfig,{ mode:"development", plugins: [ new webpack.DefinePlugin({ 'process.env.CONTRACT_ADDRESS': JSON.stringify(process.env.CONTRACT_ADDRESS), 'process.env.DEBUG': JSON.stringify(process.env.DEBUG), }), ], devServer: { historyApiFallback: true, port:8080, hot:true } });

webpack.prod.js

const webpack = require("webpack"); const baseConfig = require("./webpack.common"); const { merge } = require("webpack-merge"); module.exports = merge(baseConfig,{ mode: "production", plugins: [ new webpack.DefinePlugin({ 'process.env.CONTRACT_ADDRESS': JSON.stringify(process.env.CONTRACT_ADDRESS), 'process.env.DEBUG': JSON.stringify(process.env.DEBUG), }), ], });

 .env文件

# .env 这个文件不能提交到git中 PRIVATE_KEY=私钥地址 ALCHEMY_API_KEY=ALCHEMY的key 

package.json添加配置

 "scripts": { "dev": "webpack serve --config webpack.dev.js", "build": "webpack --config webpack.prod.js", "deploy:local": "npx hardhat run scripts/deploy-counter.ts --network localhost" }

2.solidity代码

在contracts目录下新建Counter.sol文件

pragma solidity ^0.8.24; import "hardhat/console.sol"; contract Counter { uint counter; event CounterInc(uint counter); function count() public { counter++; console.log("Now, counter is: ", counter); emit CounterInc(counter); } function getCount() public view returns (uint) { return counter; } } 


 

3.编写部署代码

在package.json所有目录下新增scripts目录,创建deploy-counter.ts文件

import "@nomicfoundation/hardhat-ethers"; import { ethers } from "hardhat"; async function deploy() { const Counter = await ethers.getContractFactory("Counter"); const counter = await Counter.deploy(); await counter.waitForDeployment(); console.log('counter address is', await counter.getAddress()); return counter; } async function count(counter: any) { await counter.count(); console.log('count is',await counter.getCount()); } deploy().then(count);

4.连接小狐狸钱包

  创建一个钱包,打开谷歌浏览器,输入地址:https://metamask.io/zh-CN/download

我选择添加扩展

然后点击:

就可以找到狐狸钱包了

我们选创建一个钱包

要用 Google/Apple(因为没有备份可恢复),对于大多数加密货币用户:建议只用助记词方式,完全自主控制,不依赖任何第三方服务。Google/Apple 方式适合追求便捷的轻度用户,但要知道其中的信任依赖。

屏幕显示 12 个单词 → 立即手抄!,不要拍照,谁拿到你的助记词,谁就等于拥有了你的全部加密资产!

首先在根目录执行,启动一个 本地以太坊区块链节点(只在你电脑上运行),让前端 DApp 能通过 MetaMask 与本地合约交互,让你能在真实环境中测试 DApp,而无需花费真钱或等待测试网确认。记住这些只是测试工具,不要与真实钱包混淆!

  • 自动生成 20 个测试账户,每个账户包含:
    • 一个 私钥
    • 一个 地址
    • 10,000 个测试 ETH(仅限本地使用,没有真实价值)
npx hardhat node

注意这个地址: http://127.0.0.1:8545/,配置网络:

       

导出测试的私钥

把控制台打印的私钥复制进来

导入后:

  • MetaMask 账户 = Hardhat 账户
  • 有钱 + 有权限 → 开发调试畅通无阻!

5.启动 Webpack 开发服务器

首先部署合约

# 在另一个终端窗口部署合约 npx hardhat run scripts/deploy-counter.ts --network localhost 
D:\ZEEKLOG\Hardhat2.22.17>npx hardhat run scripts/deploy-counter.ts --network localhost counter address is 0x5FbDB2315678afecb367f032d93F642f64180aa3 count is 1n D:\ZEEKLOG\Hardhat2.22.17> 

启动 Webpack 开发服务器

npx webpack serve --config webpack.dev.js
D:\ZEEKLOG\Hardhat2.22.17>npx webpack serve --config webpack.dev.js [[email protected]] injecting env (0) from .env -- tip: 🔐 encrypt with Dotenvx: https://dotenvx.com <i> [webpack-dev-server] Project is running at: <i> [webpack-dev-server] Loopback: http://localhost:8080/, http://[::1]:8080/ <i> [webpack-dev-server] On Your Network (IPv4): http://198.18.0.1:8080/ <i> [webpack-dev-server] On Your Network (IPv6): http://[fdfe:dcba:9876::1]:8080/ <i> [webpack-dev-server] Content not from webpack is served from 'D:\ZEEKLOG\Hardhat2.22.17\public' directory <i> [webpack-dev-server] 404s will fallback to '/index.html' asset main.js 1.49 MiB [emitted] (name: main) asset index.html 242 bytes [emitted] runtime modules 27.9 KiB 12 modules modules by path ./node_modules/ethers/lib.commonjs/ 889 KiB 118 modules modules by path ./node_modules/ethers/node_modules/ 179 KiB 20 modules modules by path ./node_modules/aes-js/lib.commonjs/*.js 66.8 KiB ./node_modules/aes-js/lib.commonjs/index.js 1.6 KiB [built] [code generated] + 8 modules modules by path ./node_modules/webpack-dev-server/client/ 84.8 KiB modules by path ./node_modules/webpack-dev-server/client/*.js 53.3 KiB 4 modules modules by path ./node_modules/webpack-dev-server/client/utils/*.js 980 bytes 2 modules + 2 modules modules by path ./node_modules/webpack/hot/*.js 5.17 KiB ./node_modules/webpack/hot/dev-server.js 1.94 KiB [built] [code generated] ./node_modules/webpack/hot/log.js 1.73 KiB [built] [code generated] + 2 modules + 5 modules webpack 5.103.0 compiled successfully in 3441 ms

在浏览器中输入:http://localhost:8080

点击连接

点击'increment'按钮

点"确认"代表你在授权一个区块链交易!这是 MetaMask 交易确认弹窗

弹窗信息解读

✅ 网络:Hardhat Localhost(本地测试网)
✅ 请求来自:localhost:8080(你的前端DApp)
✅ 交互对象:合约地址(0x5FbDB...80aa3)
✅ 网络费:<US$0.01(测试币,实际$0)

const btn = document.createElement("button");
btn.innerHTML = "increment";
btn.onclick = async function () {
    await contract.count();  // ⬅️ 这里触发的!
}

// 点击确认后:
1. ✅ 发送一个交易到 Counter 合约
2. ✅ 调用 count() 函数
3. ✅ 合约中的计数器 +1
4. ✅ 触发 CounterInc 事件
5. ✅ 前端监听到事件,更新显示

区块链层面的操作:

// Counter.sol 合约中的函数
function count() public {
    count += 1;                // 状态变量+1
    emit CounterInc(count);    // 发出事件
}

// 这个操作会:
1. 🔄 修改区块链状态(count值改变)
2. 📝 产生一个交易记录
3. ⛓️ 被添加到区块中
4. 🔥 消耗少量 Gas(测试币)

至此,前端可以调用到合约了

6.部署到Alchemy测试网

打开网址:https://dashboard.alchemy.com/apps/vatwoz0eoicb1mih/setup

 

需要注意的是,最好弄一个gmail邮箱,qq邮箱容易被拦截。注册成功后

只选这个就行

下一步

去小狐狸复制私钥,选择这三个小点都可以:

.env文件,把私钥填写到:

从Endpoint URL复制:https://eth-sepolia.g.alchemy.com/v2/nr6k9FBd4Rvwi83XSr6xR

ALCHEMY_API_KEY就是:nr6k9FBd4Rvwi83XSr6xR

PRIVATE_KEY=填写刚刚复制的私钥,添加前辍0x
ALCHEMY_API_KEY=nr6k9FBd4Rvwi83XSr6xR

编辑hardhat.config.ts文件:

import "hardhat-gas-reporter" import "@nomicfoundation/hardhat-toolbox"; import "@nomicfoundation/hardhat-ethers"; import "@nomicfoundation/hardhat-verify"; const dotenv = require('dotenv'); dotenv.config(); type Config = import('hardhat/config').HardhatUserConfig const config: Config = { solidity: "0.8.28", networks: { hardhat: { chainId: 31337 }, sepolia_eth: { url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, accounts: [process.env.PRIVATE_KEY] } // 如果没有配置环境变量,这里先注释掉执行 // sepolia_eth: { // url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY}`, // accounts: [process.env.PRIVATE_KEY] // } }, gasReporter: { enabled: true } }; export default config; 

tsconfig.json文件,把这个"strict": true,注释掉

{ "compilerOptions": { "target": "es2020", "module": "commonjs", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, //"strict": true, "skipLibCheck": true, "resolveJsonModule": true } } 

运行命令:

npx hardhat run scripts/deploy-counter.ts --network sepolia_eth

成功了

D:\ZEEKLOG\Hardhat2.22.17>npx hardhat run scripts/deploy-counter.ts --network sepolia_eth [[email protected]] injecting env (2) from .env -- tip: 🔄 add secrets lifecycle management: https://dotenvx.com/ops [[email protected]] injecting env (0) from .env -- tip: 🔑 add access controls to secrets: https://dotenvx.com/ops counter address is 0x920F394f09E6B17133e94c7AF0a86dF5A5cB357B count is 0n D:\ZEEKLOG\Hardhat2.22.17>
输入网址查询:https://sepolia.etherscan.io/

成功上测试网了,注意,不是主网

主网是:https://etherscan.io/

Read more

WebSocket:告别轮询,实现Web实时通信 WebRTC:无需插件,实现浏览器端实时音视频通信

WebSocket:告别轮询,实现Web实时通信 WebRTC:无需插件,实现浏览器端实时音视频通信

目录 一、HTTP 协议的缺点和解决方案 二、如何实现服务器主动发数据 ①:HTTP定时轮询 ②:HTTP长轮询机制 三、WeSocket的由来 四、如何建立websocket链接 五、websocket的实现方式 六、关于WebRTC(Web Real-Time Communication) 七、流程设计 核心原理: 八、WebRTC项目搭建与依赖配置 步骤1: 服务端开发(一)—— 项目搭建与依赖配置 步骤2: 服务端开发(二)—— 核心实体类与消息处理器 步骤3: Netty 服务启动类与 SpringBoot 启动类 步骤 4: Vue前端开发 1.编辑模版template 2.编写核心脚本(script setup),实现交互逻辑 步骤5: 补充 “挂断”

OpenClaw 中 web_search + web_fetch 最佳实践速查表

OpenClaw 中 web_search + web_fetch 最佳实践速查表

OpenClaw 中 web_search + web_fetch 最佳实践速查表 摘要:本文帮助读者明确 OpenClaw 网络搜索工具和不同搜索技能的的职责边界,理解“先搜索、再抓取、后总结”的最佳实践,并能更稳定地在 OpenClaw 中使用 tavily-search 与 web_fetch 完成网络信息搜索任务。主要内容包括:解决 OpenClaw 中 web_search、tavily-search、web_fetch、原生 provider 与扩展 skill 容易混淆的问题、网络搜索能力分层说明、OpenClaw 原生搜索 provider 与 Tavily/Firecrawl 扩展 skill 的区别、标准工作流、提示词模板、

Java Web 开发:JSON 基础 + @Test 测试 + Cookie/Session/ 请求处理

Java Web 开发:JSON 基础 + @Test 测试 + Cookie/Session/ 请求处理

个人主页:♡喜欢做梦 欢迎  👍点赞  ➕关注  ❤️收藏  💬评论 目录 编辑 🍍JSON的概念  🍐概念  🍐@Test注解 🍑什么是@Test? 🍑与JSON关联 🍑@Test标记的方法与main方法的区别  🍍JSON语法  🍐核心数据类型  🍐常见使用 🍑对象 🍑数组  🍑JSON字符串和Java对象的互转 🍑传递JSON 🍑获取URL中的参数 🍑上传文件:@RequestPart  🍍Cookie和Seeion  🍐Cookie 🍑什么是Cookie? 🍑Cookie的获取  🍐Session 🍑什么是Session?  🍐Cookie和Session之间的关系 🍑Session的存储 🍑Session的获取 🍍获取header 🍍JSON的概念  🍐概念 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。他基于JavaScript的一个子集,但采用了独立语言的文

【LLM】Ollama:本地大模型 WebAPI 调用实战指南

1. 为什么选择Ollama部署本地大模型 最近两年大模型技术发展迅猛,但很多开发者面临一个现实问题:公有云API调用不仅费用高昂,还存在数据隐私风险。Ollama的出现完美解决了这个痛点,它就像是你本地的模型管家,可以一键部署各种开源大模型。我去年在开发智能客服系统时就深受其益,既避免了敏感客户数据外泄,又省下了大笔API调用费用。 与传统方案相比,Ollama有三大优势:首先是安装简单,用Docker一条命令就能跑起来;其次是模型丰富,支持Llama、Mistral等主流开源模型;最重要的是API标准化,完全兼容OpenAI的接口规范。实测在16GB内存的MacBook Pro上运行7B参数的模型,响应速度可以控制在2秒以内,完全能满足大多数应用场景。 2. 五分钟快速搭建Ollama环境 2.1 准备工作就像搭积木 在开始之前,我们需要准备两个基础组件:Docker和Python环境。这里有个小技巧分享——建议使用Docker Desktop的WSL2后端(Windows用户),性能比传统虚拟机模式提升30%以上。安装完成后,记得执行以下命令验证版本: docker