You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
9.5 KiB
403 lines
9.5 KiB
import Web3 from "web3";
|
|
import IPPTAbi from './ippt-abi.json'
|
|
import USDTAbi from './usdt-abi.json'
|
|
|
|
// 将科学计数法形式的数字或字符串转换为可读的字符串
|
|
function scientificNotationToString(param) {
|
|
let strParam = String(param);
|
|
let flag = /e/.test(strParam);
|
|
if (!flag) return param.toString();
|
|
|
|
// 指数符号 true: 正,false: 负
|
|
let sysbol = true;
|
|
if (/e-/.test(strParam)) {
|
|
sysbol = false;
|
|
}
|
|
// 指数
|
|
let index = Number(strParam.match(/\d+$/)[0]);
|
|
// 基数
|
|
let basis = strParam.match(/^[\d\.]+/)[0].replace(/\./, "");
|
|
|
|
if (sysbol) {
|
|
return basis.padEnd(index + 1, "0");
|
|
} else {
|
|
return basis.padStart(index + basis.length, "0").replace(/^0/, "0.");
|
|
}
|
|
}
|
|
|
|
// 将数字转换到指定精度
|
|
function toDecimals(value, decimals) {
|
|
return Number(value) * 10 ** Number(decimals);
|
|
}
|
|
|
|
// 从指定精度转换数字
|
|
function fromDecimals(value, decimals) {
|
|
return value / Math.pow(10, decimals);
|
|
}
|
|
|
|
class ERC20 {
|
|
decimals = 0;
|
|
|
|
/**
|
|
* 合约实例
|
|
* @type {Contract}
|
|
*/
|
|
contract;
|
|
|
|
/**
|
|
* 合约地址
|
|
* @type {string}
|
|
*/
|
|
contractAddress;
|
|
|
|
/**
|
|
* 合约 ABI
|
|
*/
|
|
abi;
|
|
|
|
// 当前的钱包地址, 当 connected 为 true 时可用
|
|
get selectedAddress () {
|
|
return window.ethereum.selectedAddress;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return {*}
|
|
*/
|
|
get ethereum() {
|
|
return window.ethereum
|
|
}
|
|
|
|
/**
|
|
* @param contractAddress{string}
|
|
* @param abi{Array}
|
|
* @param web3{Web3}
|
|
*/
|
|
constructor (contractAddress, abi, web3) {
|
|
this.contractAddress = contractAddress;
|
|
this.abi = abi;
|
|
this.contract = new web3.eth.Contract(abi, contractAddress);
|
|
}
|
|
|
|
/**
|
|
* 加密 abi
|
|
* @param methodDefinition {string} - 方法名
|
|
* @param parameters {Array<{type: "uint256" | "address"; value: number | string}>} - 参数
|
|
* @return {string}
|
|
*/
|
|
encodeABI(methodDefinition, parameters) {
|
|
let m = Web3.utils.sha3(methodDefinition).slice(0, 10);
|
|
parameters.forEach((p) => {
|
|
switch (p.type) {
|
|
case "uint256":
|
|
case "address":
|
|
m += p.value
|
|
.toString()
|
|
.toLowerCase()
|
|
.replace("0x", "")
|
|
.padStart(64, "0");
|
|
break;
|
|
}
|
|
});
|
|
return m;
|
|
}
|
|
|
|
handleError(err) {
|
|
const message = err ? err.message ? err.message : err.toString() : 'Unknown Error'
|
|
if (message.includes("User denied")) {
|
|
uni.showToast({
|
|
title: 'failed',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 相当于 contract.methods.xx.send
|
|
* @param contractSendMethod
|
|
* @param sendOptions
|
|
* @return {PromiEvent<Eth.Contract>}
|
|
*/
|
|
send (contractSendMethod, sendOptions) {
|
|
const promiEvent = contractSendMethod.send(sendOptions);
|
|
promiEvent.catch(err => this.handleError(err));
|
|
return promiEvent;
|
|
}
|
|
|
|
|
|
/**
|
|
* 获取代币余额
|
|
*
|
|
* @param account{string}
|
|
*
|
|
* @return Promise<number>
|
|
*/
|
|
async $balanceOf (account) {
|
|
const balance = await this.contract.methods.balanceOf(account).call({from: this.selectedAddress})
|
|
const decimals = await this.$decimals()
|
|
return Number(fromDecimals(Number(balance), decimals))
|
|
}
|
|
|
|
/**
|
|
* 获取授权给某个合约的数量
|
|
* @param owner {string} - 钱包地址
|
|
* @param spender {string} - 授权地址
|
|
* @return {Promise<number>}
|
|
*/
|
|
$allowance(owner, spender) {
|
|
return this.contract.methods.allowance(owner, spender).call()
|
|
.then(async (res) => {
|
|
return Number(fromDecimals(Number(res), await this.$decimals()))
|
|
})
|
|
}
|
|
|
|
|
|
/**
|
|
* 获取 vnft 合约的精度
|
|
*
|
|
* @return Promise<number>
|
|
*/
|
|
$decimals () {
|
|
if (this.decimals) {
|
|
return Promise.resolve(this.decimals)
|
|
}
|
|
return this.contract.methods
|
|
.decimals()
|
|
.call()
|
|
.then(res => {
|
|
this.decimals = Number(res)
|
|
return this.decimals
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 授权代币给某个合约
|
|
* @param spender - 授权地址
|
|
* @param amount - 授权金额
|
|
* @return {PromiEvent<Contract>}
|
|
*/
|
|
$approve(spender, amount) {
|
|
amount = toDecimals(amount, this.decimals);
|
|
return this.send(this.contract.methods.approve(spender, scientificNotationToString(amount)), {from: this.selectedAddress})
|
|
}
|
|
|
|
/**
|
|
* 授权最大数量
|
|
* @param spender
|
|
* @return {PromiEvent<Eth.Contract>}
|
|
*/
|
|
approveMAX(spender) {
|
|
return this.send(this.contract.methods.approve(spender, "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), {from: this.selectedAddress})
|
|
}
|
|
|
|
async $burn(amount, sendOptions) {
|
|
amount = toDecimals(amount, await this.$decimals());
|
|
return this.send(this.contract.methods.burn(amount), sendOptions);
|
|
}
|
|
|
|
/**
|
|
* 转移 token
|
|
* @param recipient - 接受者
|
|
* @param amount - 转移数量
|
|
* @return {PromiEvent<Eth.Contract>}
|
|
*/
|
|
async $transfer(recipient, amount) {
|
|
amount = toDecimals(amount, await this.$decimals());
|
|
// debugger
|
|
return this.send(this.contract.methods.transfer(recipient, amount.toString()), {from: this.selectedAddress});
|
|
}
|
|
|
|
}
|
|
|
|
class USDT extends ERC20 {
|
|
decimals = 18;
|
|
}
|
|
|
|
class IPPT extends ERC20 {
|
|
/**
|
|
* 调用IPPT 合约绑定上级关系
|
|
* creatCode(address superior, address plivate) public returns (bool)
|
|
* @return {PromiEvent<Eth.Contract>}
|
|
*/
|
|
creatCode (superior, plivate) {
|
|
return this.send(this.contract.methods.creatCode(superior, plivate), {from: this.selectedAddress});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* // 链接钱包, 所有 web3x.xx 方法都需要先执行 connectViaInPage 成功
|
|
* web3x.connectViaInPage()
|
|
* .then(res => {
|
|
* // 当前钱包地址
|
|
* console.log("当前钱包地址", web3x.selectedAddress);
|
|
*
|
|
* // 授权当前钱包的 USDT 给 IPPT (参数为 IPPT 合约地址)
|
|
* web3x.usdt.approveMAX("0x6b175474e89094c44da98b954eedeac495271d0f")
|
|
* .on("confirmation", hash => {
|
|
* console.log("授权成功", hash);
|
|
* })
|
|
*
|
|
* // 调用IPPT 合约绑定上级关系
|
|
* // 0xFb4FC7Ddb8c4aa6b944703CE1e89D2B9Aa67a400: 上级地址
|
|
* // web3x.selectedAddress: 当前钱包地址
|
|
* web3x.ippt.$creatCode("0xFb4FC7Ddb8c4aa6b944703CE1e89D2B9Aa67a400", web3x.selectedAddress)
|
|
* .on("confirmation", hash => {
|
|
* console.log("绑定上级关系成功", res);
|
|
* })
|
|
* })
|
|
* // 切换到 BSC 测试网, 需先调用 web3x.addBscTestnet()
|
|
* web3x.switchToBscTestnet
|
|
* // 添加 BSC 测试网
|
|
* web3x.addBscTestnet
|
|
**/
|
|
class Web3X {
|
|
/**
|
|
* 是否已链接
|
|
* @type {boolean}
|
|
*/
|
|
connected = false;
|
|
|
|
/**
|
|
* web3 实例
|
|
* @type {Web3}
|
|
*/
|
|
#web3;
|
|
|
|
|
|
/**
|
|
* @type {IPPT}
|
|
*/
|
|
ippt
|
|
|
|
/**
|
|
* @type {ERC20}
|
|
*/
|
|
usdt
|
|
|
|
/**
|
|
* @type {string}
|
|
*/
|
|
ipptContractAddress
|
|
|
|
/**
|
|
* usdt 合约地址
|
|
* @type {string}
|
|
*/
|
|
usdtContractAddress
|
|
|
|
constructor (ipptContractAddress, usdtContractAddress) {
|
|
this.ipptContractAddress = ipptContractAddress;
|
|
this.usdtContractAddress = usdtContractAddress;
|
|
}
|
|
|
|
// 当前的钱包地址, 当 connected 为 true 时可用
|
|
get selectedAddress () {
|
|
return window.ethereum.selectedAddress;
|
|
}
|
|
|
|
/**
|
|
* 是否存在运行环境中
|
|
* @return {boolean}
|
|
*/
|
|
hasRuntime () {
|
|
return !!window.ethereum;
|
|
}
|
|
|
|
// 调用 rpc 方法
|
|
async #request (method, params) {
|
|
if (!this.hasRuntime()) {
|
|
throw new Error('No runtime');
|
|
}
|
|
return await window.ethereum.request({
|
|
method,
|
|
params,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* switch to a new chain (by chain ID)
|
|
*/
|
|
async switchChain (chainId) {
|
|
return await this.#request('wallet_switchEthereumChain', [{ chainId }]);
|
|
}
|
|
|
|
/**
|
|
* add a new chain (by chain object)
|
|
*/
|
|
async addChain (chainParams) {
|
|
return await this.#request('wallet_addEthereumChain', [chainParams]);
|
|
}
|
|
|
|
/**
|
|
* 切换到BSC 测试网, 需先调用 web3x.addBscTestnet()
|
|
* @example
|
|
* web3x.connectViaInPage()
|
|
* .then(() => {
|
|
* web3x.addBscTestnet()
|
|
* .then(() => {
|
|
* web3x.switchToBscTestnet()
|
|
* })
|
|
* })
|
|
*/
|
|
async switchToBscTestnet () {
|
|
return await this.switchChain('0x61');
|
|
}
|
|
|
|
/**
|
|
* 添加 BSC 测试网
|
|
* @example
|
|
* web3x.connectViaInPage()
|
|
* .then(() => {
|
|
* web3x.addBscTestnet()
|
|
* .then(() => {
|
|
* web3x.switchToBscTestnet()
|
|
* })
|
|
* })
|
|
*/
|
|
async addBscTestnet () {
|
|
return await this.addChain({
|
|
chainId: '0x61',
|
|
chainName: 'Binance Smart Chain Testnet',
|
|
nativeCurrency: {
|
|
name: 'BNB',
|
|
symbol: 'bnb',
|
|
decimals: 18,
|
|
},
|
|
rpcUrls: ['https://data-seed-prebsc-1-s1.binance.org:8545/'],
|
|
blockExplorerUrls: ['https://testnet.bscscan.com'],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 链接钱包
|
|
* @return {Promise<string[]>} - 16 进制的钱包地址数组
|
|
*/
|
|
async connectViaInPage () {
|
|
return this.#request('eth_requestAccounts')
|
|
.then(res => {
|
|
this.#web3 = new Web3(window.ethereum);
|
|
this.ippt = new IPPT(this.ipptContractAddress, IPPTAbi, this.#web3);
|
|
this.usdt = new USDT(this.usdtContractAddress, USDTAbi, this.#web3);
|
|
this.connected = true;
|
|
return Promise.resolve(res)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 监听钱包地址改变
|
|
* @param callback{() => void} - 回调函数, 参数为新的钱包地址
|
|
*/
|
|
onAccountsChanged (callback) {
|
|
return window.ethereum.on('accountsChanged', callback)
|
|
}
|
|
|
|
}
|
|
|
|
export const web3x = new Web3X(
|
|
// BSC链连接是https://endpoints.omniatech.io/v1/bsc/mainnet/public
|
|
// BSC链Id是56
|
|
// ippt 合约
|
|
"0x622d7b79a904e00e5fcab06396ff009e441f0186",
|
|
// usdt 合约 0x0a70dDf7cDBa3E8b6277C9DDcAf2185e8B6f539f
|
|
"0x55d398326f99059fF775485246999027B3197955"
|
|
);
|
|
|