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);
}
复制代码

 

posted @   龙卷风吹毁停车场  阅读(1030)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
历史上的今天:
2021-04-27 element 设置table表头样式
2020-04-27 js使用正则表达式匹配富文本框中的img标签
点击右上角即可分享
微信分享提示