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

用 Web 技术构建跨平台应用:Capacitor 完全指南

开篇的碎碎念:自从发现了capacitor,我就一直在用!不用像 Flutter 那样配置一堆环境,也不用学习新的 UI 写法,直接用前端三件套就能打包原生 App。简直是降维打击!那么接下来就开始capacitor的学习吧!!! 目录 速成版 1. 什么是 Capacitor?         1.1 定义与背景         1.2 为什么叫 Capacitor?         1.3 发展历程         1.4 核心理念 2. Capacitor vs 其他跨平台方案         2.1 横评对比         2.2 为什么选择 Capacitor? 3. Capacitor 核心架构         3.1 架构图         3.2

Altium Designer导入DXF/DWG文件常见问题与实战解决方案

1. 导入失败:版本兼容性与文件损坏问题 我在使用Altium Designer导入DXF/DWG文件时,最常遇到的就是导入失败的情况。软件弹窗提示"由于文件版本不兼容或文件损坏而无法打开",这种情况特别让人头疼,尤其是赶项目的时候。 根本原因在于CAD和Altium Designer之间的版本鸿沟。AutoCAD每年都会推出新版本,而Altium Designer的更新节奏跟不上,这就导致了高版本的DWG文件在AD中无法识别。我实测过,AD 16.1版本最高只能兼容到AutoCAD 2013格式,再新的版本就会报错。 解决方案其实很简单:在AutoCAD中另存为低版本格式。我建议保存为2004或2007版本的DXF文件,这两个版本在兼容性方面表现最稳定。具体操作:在AutoCAD中打开文件后,点击"另存为",在文件类型中选择"AutoCAD 2004/LT2004 DXF (*.dxf)"。这个办法我用了十年,几乎能解决90%的导入失败问题。 如果保存为低版本后仍然无法导入,可能是文件本身损坏了。这时候可以在AutoCAD中使用RECOVER命令修复文件,然后再重新保存为低版

快学快用系列:一文学会java后端WebApi开发

快学快用系列:一文学会java后端WebApi开发

文章目录 * 第一部分:Web API开发基础概念 * 1.1 什么是Web API * 1.2 RESTful API设计原则 * 第二部分:开发环境搭建 * 2.1 环境要求 * 2.2 创建Spring Boot项目 * 2.3 配置文件 * 第三部分:项目架构设计 * 3.1 分层架构 * 3.2 包结构设计 * 第四部分:数据模型设计 * 4.1 实体类设计 * 4.2 DTO设计 * 第五部分:数据访问层实现 * 5.1 Repository接口 * 5.2 自定义Repository实现 * 第六部分:业务逻辑层实现

【Web】深入解析C3P0反序列化漏洞——Hex字节码加载与防御策略

1. C3P0反序列化漏洞概述 C3P0作为Java生态中广泛使用的JDBC连接池组件,其反序列化漏洞近年来在安全领域备受关注。这个漏洞的特殊之处在于,它允许攻击者在不出网(无需外部网络连接)的情况下,通过精心构造的Hex字节码实现远程代码执行。我在实际渗透测试中多次遇到这个漏洞,发现它常与Fastjson、Jackson等流行框架组合出现,形成完整的攻击链。 漏洞的核心在于WrapperConnectionPoolDataSource类对userOverridesAsString属性的处理机制。当这个属性以"HexAsciiSerializedMap"开头时,C3P0会将其后的十六进制字符串转换为字节数组并进行反序列化操作。这种设计原本是为了支持连接池配置的灵活存储,却意外成为了安全突破口。 2. Hex字节码加载机制深度解析 2.1 漏洞触发流程分析 让我们通过一个真实案例来理解漏洞触发过程。去年在某次企业安全评估中,我发现一个使用Fastjson 1.2.47的Web服务,配合C3P0 0.9.5.2版本,形成了完美的攻击面。攻击者只需要发送如下JSON数据: {