IPPSWAP-h5
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

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