前端调用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

--- 通过逆向 WebChat 协议打造 OpenClaw 的“万能胶水” ---

摘要 在 OpenClaw 的二次开发中,官方推荐的 Channel 扩展模式往往伴随着较高的开发和部署成本。本文介绍了一种更直接的“降维打击”方案:通过逆向工程解析 Gateway 与 WebChat 之间的 WebSocket 通信协议,构建一个通用适配器(Universal Adapter)。该适配器能将任何外部程序(CLI、脚本、第三方 UI)伪装成官方 WebChat 客户端,从而实现零后端修改接入,并天然支持会话历史同步。 正文内容 1. 缘起:为什么我们需要这层“胶水”? 在 OpenClaw 的生态中,如果你想让一个外部系统(比如一个 Python 脚本、一个 IoT 设备或者一个自定义网页)和 Agent 对话,官方的标准答案通常是:“去开发一个

OpenWebUI联网搜索实战:如何用SearXNG让本地大模型获取实时信息(附百度/360配置)

OpenWebUI联网搜索实战:如何用SearXNG让本地大模型获取实时信息(附百度/360配置) 如果你在本地运行大模型,比如用Ollama部署了Qwen、Llama或者DeepSeek,可能会发现一个尴尬的问题:模型的知识截止日期是固定的,它不知道今天股市涨跌,不清楚最新的科技新闻,甚至不知道明天是什么节日。这种“信息孤岛”的感觉,让本地大模型的实用性大打折扣。 我最初搭建OpenWebUI环境时,也遇到了这个痛点。看着模型一本正经地分析过时的数据,那种无力感让我开始寻找解决方案。市面上有不少联网搜索方案,但要么配置复杂,要么对国内网络环境不友好。经过几周的折腾和测试,我发现SearXNG这个开源元搜索引擎,配合OpenWebUI的联网搜索功能,是目前最稳定、最灵活的方案之一。 更重要的是,通过合理配置SearXNG,我们可以让本地大模型直接调用百度、360等国内搜索引擎,获取符合中文用户习惯的实时信息。这不仅仅是技术上的连接,更是让本地AI真正“接地气”的关键一步。下面我就把自己踩过的坑、验证过的配置,以及实际效果对比,毫无保留地分享给你。 1. 为什么需要SearXN

【硬核排查】挂了代里还是“裸奔”?深度解析 WebRTC 泄露与 Google 账号风控机制

【硬核排查】挂了代里还是“裸奔”?深度解析 WebRTC 泄露与 Google 账号风控机制

本文仅用于技术研究,禁止用于非法用途。 Author:枷锁 前言:一个“玄学”的网络故障 最近在进行网络环境配置时遇到了一个非常反直觉的现象: 我在本地开启了 戴笠,状态栏显示连接正常,访问Gemini毫无压力。但是,当我打开 ip138 或百度搜索 “IP” 时,显示的却依然是我本地的 ISP 真实 IP。更糟糕的是,我的 Google 账号开始频繁触发安全风控——要么是登录时无限弹出验证码,要么是刚登上去就被踢下线。 这不仅仅是“连不上”的问题,而是一个典型的网络协议泄露与安全风控案例。本着“知其然更要知其所以然”的精神,我深扒了其背后的技术原理,发现罪魁祸首主要有两个:路由分流策略与WebRTC 协议漏洞。 第一部分:为什么 ip138 “出卖”了你?—— 聊聊路由分流 (Split Tunneling) 很多新手判断 是否生效的标准是:

2025版最详细WebStorm下载安装教程(详细图解)

2025版最详细WebStorm下载安装教程(详细图解)

目录 一、前言 二、WebStorm的下载安装 1、下载WebStorm 2、安装WebStorm 3、首次启动WebStorm 一、前言 前端一般就是用WebStorm或者是VSCode,Jetbrains家的ide一般都比较重,VSCode相对而言就轻快一点。主要还是看大家自己喜欢哪个就下哪个,我个人电脑内存是32G所以我一直用Jetbrains家的软件体验不错。本博客记录一下WebStorm的安装流程,大家自行参考 然后WebStorm从24年10月开始就是免费的了,所以不需要任何许可证直接下了就能用,并且也不需要像Java和Python那样配JDK和解释器,整体还是很简单的 二、WebStorm的下载安装 1、下载WebStorm 打开浏览器,访问JetBrains的官方网址,点击如下网址能直接跳转到WebStorm的下载页面: Download WebStorm: The JavaScript and TypeScript IDE by JetBrains 选择好自己的系统,然后直接点击Download即可 等待安装包下载完成,网速快