web3技术、切换metamask网络、查询metamask余额、计算gas手续费、metamask发起交易,判断连接网络是否正确等.........
在assets下新建web3.js文件
import { Message } from 'element-ui'; import { ethers } from "ethers"; //版本号为 "ethers": "^4.0.47", import Tabi from './Tabi.json'; //后端给 import { infoChain } from '@/api/require' //后端接口,获取要添加的链的 {chainId,chainName,nativeCurrency,rpcUrls,blockExplorerUrls} /** * window.ethereum.selectedAddress (异步获取) 获取metamask钱包地址(返回字符串) * const accounts = await ethereum.request({ method: 'eth_accounts' }) (同步获取)获取metamask钱包地址返回的数组 * const accounts = await ethereum.request({ method: 'eth_requestAccounts' }); 连接metamask钱包 * const chainId = await ethereum.request({ method: 'eth_chainId' }); //!链id不是马上拿到的,如果通过链id来判断是不是主网的方式,请注意异步 */ let provider = new ethers.providers.Web3Provider(window[sessionStorage.getItem('ethereumType')] || window.ethereum); function renewProvider() { provider = new ethers.providers.Web3Provider(window[sessionStorage.getItem('ethereumType')] || window.ethereum); } /** 监听钱包地址切换 window[sessionStorage.getItem('ethereumType') || 'ethereum'].on("accountsChanged", (accounts) => { if (accounts[0]) { console.log('=========钱包切换=======') } else { console.log('钱包断开================') // 断开了 } }); */ /** * 获取当前钱包连接的地址 */ export async function getWalletAddress() { const accounts = await window[sessionStorage.getItem('ethereumType') || 'ethereum'].request({ method: 'eth_accounts' }) return accounts[0] } /** * 查询主网络资产余额 * @param {*} address 钱包地址 * @param {*} tokenDecimals token精度 */ export async function getMainNetworkBalance(address, tokenDecimals = 18) { const balancePromise = provider.getBalance(address) return balancePromise.then((balance) => { return ethers.utils.formatUnits(balance, tokenDecimals); }).catch(e => { Message({ type: 'error', showClose: true, message: 'Failed to get balance.' }) throw new Error("获取余额失败" + e) }); } // 查询余额,固定不变的 const erc20BalanceAbiFragment = [{ "constant": true, "inputs": [{ "name": "", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "", "type": "uint256" }], "type": "function" }] // token授权,固定不变的 const ERC20_ABI = [ "function allowance(address owner, address spender) external view returns (uint256)", "function approve(address spender, uint256 amount) external returns (bool)" ] /** * 添加metamask网络 */ async function addChain() { try { // infoChain 获取要添加的链的 {chainId,chainName,nativeCurrency,rpcUrls,blockExplorerUrls} const res = await infoChain() if (res.code === 0) { await window[sessionStorage.getItem('ethereumType') || 'ethereum'].request({ method: "wallet_addEthereumChain", params: [{ chainId: '0x' + res.data.chainId.toString(16), chainName: res.data.chainName, nativeCurrency: { name: res.data.suisse.symbol, symbol: res.data.suisse.symbol, decimals: 18, }, rpcUrls: [res.data.rpcUrl], blockExplorerUrls: [res.data.scanUrl], }], }); } } catch (error) { console.log(error) throw error } } /** * 切换到指定网络 * @param {*} chainId 要切换的链id */ export async function toSwitch(chainId) { try { window[sessionStorage.getItem('ethereumType') || 'ethereum'] && await window[sessionStorage.getItem('ethereumType') || 'ethereum'].request({ method: 'wallet_switchEthereumChain', params: [{ chainId: '0x' + chainId.toString(16) }] }, (err, result) => { console.log(err, '添加失败') if (err) { Message({ message: err.message, type: 'error' }) return false } }); } catch (error) { if (error.code === 4902) { addChain() } else { throw error } } } /** * 检查当前metamask 链接网络是否正确 * @param {*} chainId 传入链id * @returns {Boolean} */ export async function chainIdJudgment(chainId) { try { let eth_chainId = await window[sessionStorage.getItem('ethereumType') || 'ethereum'].request({ method: 'eth_chainId' }); //16进制 // 将16进制转为10进制 eth_chainId = parseInt(eth_chainId, 16) if (eth_chainId != chainId) { toSwitch(chainId) } else { return true } } catch (error) { console.log(error) return false } } /** * 发起交易 * @param {String} multySignAddress 后端部署合约地址 * @param {String} numbers 交易数量 * @param {Number} decimals token精度 默认18 * @param {Number} pid id * @param {String} functionNname 合约里面的方法 * * 使用: * try { * const res = await payMoney(this.statkData.stakeContractAddress, this.statkData.pid, this.LpStake) * if (res && res.hash) { * 点击metamask确认后执行一下逻辑 * let result = await res.wait() * 交易成功 * if (result.status === 1) { * 交易哈希 * result.transactionHash * } else { * alert('交易失败'); * } * } * } catch (error) { * throw (error) * } */ export async function payMoney(multySignAddress, pid, numbers, functionNname = 'deposit', decimals = 18) { // new ethers.utils.BigNumber // numbers 交易数量 // decimals token精度 // functions后面的方法 调什么方法在 Tabi.json找对应的name(问后端用那个) // encode 传几个参数在 Tabi 里面inputs数组有几个传几个,类型看internalType,值看internalType值 /** import BigNumber fom 'bignumber.js' * 使用条件:购买5个面包,一个面包价值6.89元 priceView 当面包的价格 * ethers.utils.parseEther(new BigNumber(numbers).multipliedBy(new BigNumber(priceView)).toString()) * 如何使用: 将value: '0x00' 改为 value: ethers.utils.parseEther(new BigNum................ */ let numberOfTokens if (functionNname === 'withdraw') { //解除质押 numberOfTokens = ethers.utils.parseUnits(numbers.toString(), 0); } else if (functionNname === 'deposit') { //质押 numberOfTokens = ethers.utils.parseUnits(numbers.toString(), decimals); } const pidTokens = ethers.utils.parseUnits(pid.toString(), 0); const iface = new ethers.utils.Interface(Tabi); const data = iface.functions[functionNname].encode([pidTokens, numberOfTokens]); const address = window[sessionStorage.getItem('ethereumType') || 'ethereum'].selectedAddress let transactionParameters = { to: multySignAddress, from: address, //验证合约调用需要from,必传 value: '0x00', data: data }; const failed = await validate(transactionParameters); if (failed) { console.error('failed approveERC20' + failed); return { success: false, msg: 'failed crossIn' + failed } } delete transactionParameters.from; //etherjs 4.0 from参数无效 报错 return sendTransaction(transactionParameters) } //验证交易参数 async function validate(tx) { renewProvider() try { const result = await provider.call(tx); return ethers.utils.toUtf8String("0x" + result.substr(138)); } catch (e) { // 金额不够提示 if (e.code == -32000) { if (e.message.indexOf('err: insufficient funds for gas') > -1) { Message({ type: 'error', showClose: true, message: 'You do not have enough Balance in your account to pay for transaction fees on network.' }) } else { Message({ type: 'error', showClose: true, message: e.message }) } } else { if (e.code === -32603) { Message({ type: 'error', showClose: true, message: e?.data?.message }) } else { Message({ type: 'error', showClose: true, message: e.message }) } } return false; } } async function sendTransaction(tx) { renewProvider() const wallet = provider.getSigner(); return await wallet.sendTransaction(tx); } /** * 进行授权 * @param {*} contractAddress 支付币的合约地址 * @param {*} multySignAddress 要收到支付的合约地址 * @param {*} address 钱包地址 * @param inputTokenDecimals token 精度 * * 使用方法:如下 * try { * const payment = await approveERC20(this.itemData.inputTokenContractAddress, this.itemData.idoContractAddress, address) * if (payment.hash) { * let rr = await payment.wait() * if (rr.status === 1) { * this.passThrough = false * } * } else { * this.$tool.message('Authorization failed', 'error') * } * } catch (error) { * this.$tool.message(payment.msg, 'error') * throw (error) * } */ export async function approveERC20(contractAddress, multySignAddress, address ,inputTokenDecimals) { const iface = new ethers.utils.Interface(ERC20_ABI); const data = iface.functions.approve.encode([multySignAddress, new ethers.utils.BigNumber('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')]); // 设置自定义授权额度 // let shuliang = '34' // shuliang = Math.ceil(shuliang) // const hex = new ethers.utils.parseUnits(shuliang, inputTokenDecimals) // shuliang 数量 字符串类型 // const gasLimit = new exthers.utils.parseUnits('58236',0) // const data = iface.functions.approve.encode([multySignAddress, new ethers.utils.BigNumber(hex._hex)]); const transactionParameters = { to: contractAddress, from: address, value: '0x00', data: data, gasLimit: gasLimit, }; const failed = await validate(transactionParameters); if (failed) { console.error('failed approveERC20' + failed); return { success: false, msg: 'failed approveERC20' + failed } } delete transactionParameters.from; //etherjs 4.0 from参数无效 报错 return sendTransaction(transactionParameters) // const res ={} const r = await wait() } /** * 查询是否需要授权 * @param contractAddress 支付币的合约地址 * @param multySignAddress 要收到支付的合约地址 * @param address 钱包账户地址 * @param inputTokenDecimals token 精度 * 参数里面的contractAddress 是erc20资产的合约地址 multySignAddress是要后端他们部署的那个合约地址 */ export function getERC20Allowance(contractAddress, multySignAddress, address, inputTokenDecimals) { try { const contract = new ethers.Contract(contractAddress, ERC20_ABI, provider); const allowancePromise = contract.allowance(address, multySignAddress); return allowancePromise .then(allowance => { // 查询出来的授权额度数量 const AuthorizedAmount = ethers.utils.formatUnits(allowance._hex, inputTokenDecimals) console.log(AuthorizedAmount, '已授权额度') // 自定义 let baseAllowance = ethers.utils.parseUnits('数量,字符串类型', 18).toString() baseAllowance = ethers.utils.formatUnits(baseAllowance, 18) console.log(baseAllowance, '自定义的对比额度') // 判断是否需要继续授权 if (Number(baseAllowance) <= Number(AuthorizedAmount)) { return false } else { return true } // 授权最大值 // const baseAllowance = "39600000000000000000000000000"; return allowance.lte(baseAllowance) }).catch(e => { this.$mes.closeLoading() this.$mes.message('Failed to get balance.', 'error') console.error("获取erc20资产授权额度失败" + e); return true; }); } catch (error) { console.log(error) } } /** * 查询账户余额或者查询合约余额 * @param contractAddress ERC20合约地址 * @param tokenDecimals token小数位数 * @param address 账户地址 */ export function getERC20Balance(contractAddress, tokenDecimals, address) { renewProvider() let contract = new ethers.Contract(contractAddress, erc20BalanceAbiFragment, provider); let balancePromise = contract.balanceOf(address); return balancePromise.then((balance) => { return ethers.utils.formatUnits(balance, tokenDecimals); }).catch(e => { Message({ type: 'error', showClose: true, message: 'Failed to get balance.' }) throw new Error("获取余额失败" + e) }); } // 获取gas的单价 export async function getGasPrice() { renewProvider() const gasPrice = await provider.getGasPrice(); return gasPrice.toString(); } // 预估最大会消耗多少gas export async function estimateGas(tx) { try { renewProvider() const failed = await validate(tx); if (failed) { console.error('failed approveERC20' + failed); return { success: false, msg: 'failed approveERC20' + failed } } const gasLimit = await provider.estimateGas(tx); return gasLimit.add(10000).toString(); } catch (e) { console.log(e, 'fail to estimateGas, use the defaultGasLimit'); return '150000'; } } /** * 查询gas费(交易手续费) * @param tx 参数是发交易的transactionParameters参数 * @returns gas费 */ export async function getFee(multySignAddress, pid, numbers, _this, functionNname = 'deposit') { let numberOfTokens if (functionNname === 'deposit') { numberOfTokens = ethers.utils.parseUnits(numbers.toString(), 18); } else if (functionNname === 'withdraw') { numberOfTokens = ethers.utils.parseUnits(numbers.toString(), 0); } const pidTokens = ethers.utils.parseUnits(pid.toString(), 0); const iface = new ethers.utils.Interface(Tabi); const data = iface.functions[functionNname].encode([pidTokens, numberOfTokens]); const address = window[sessionStorage.getItem('ethereumType') || 'ethereum'].selectedAddress let transactionParameters = { to: multySignAddress, from: address, //验证合约调用需要from,必传 value: '0x00', data: data }; const gasPrice = await getGasPrice(); const gasLimit = await estimateGas(transactionParameters); // accMul 乘法处理精度问题 const bigNumberFee = _this.$tool.accMul(gasPrice, gasLimit); return ethers.utils.formatEther(bigNumberFee); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2021-04-27 element 设置table表头样式
2020-04-27 js使用正则表达式匹配富文本框中的img标签