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);
}

浙公网安备 33010602011771号