import { Blockchain } from "@/chain/blockchain"; import { getNonce } from "@/api/User"; import pinia from "@/store"; import { useAppStore } from "@/store/app"; import { useUserStore } from "@/store/user"; import { message } from "ant-design-vue"; import { Chain } from "@/chain/Chain"; import { AVAILABLE_CHAINS } from "@/configs/configchain"; import { AllChains } from "@/configs/allchain"; import { ACTIVATE_PROXY_ABI, MYSTERY_BOX_ABI, MYSTERY_PROXY_ABI } from "@/configs/contracts"; const AppModule = useAppStore(pinia); const UserModule = useUserStore(); export default class ChainManager { bc: Blockchain; instanceMap: Map; public chainMap: Map = new Map(); private _availableChains: Map = new Map(); constructor() { this.bc = new Blockchain(); this.instanceMap = new Map(); for (const data of AllChains) { this.chainMap.set(data.id, data); } } get availableChains() { if (this._availableChains.size === 0) { for (const id of AVAILABLE_CHAINS) { const d = this.chainMap.get(id); if (d) { this._availableChains.set(id, d); } } } return this._availableChains; } public async init() { if (this.bc.isWalletConnect) { try { await this.bc.connect(); } catch (err) { console.log("connect chain error: ", err); } } } get isLogined() { return !!UserModule.token && !!AppModule.step; } public async logout() { await this.bc.disconnect(); } public get currentChain() { return this.bc.currentChain; } public async login() { if (!AppModule.step) { try { await this.bc.connect(true); await this.checkNance(); } catch (err) { message.error(err.message, 5); await this.bc.disconnect(); await Promise.reject(err); } } } public async checkNance() { try { const preRequest: any = await getNonce(); let nonce = preRequest.nonce + ""; const tips = preRequest.data?.tips; AppModule.updateNonce(nonce); await UserModule.Login({ bcInstance: this.bc, account: AppModule.accountId, chainId: AppModule.chainId, nonce, tips, }); AppModule.updateStep(1); } catch (err) { console.log(err); await Promise.reject(err); } } public async getInstance(address: string, chainId: number, abi?: any) { const key = `${chainId}_${address}`; if (!this.instanceMap.has(key)) { const chain = new Chain(this.chainMap.get(chainId)!.rpc); const coinInstance = await chain.initContractInstance(address, abi); this.instanceMap.set(key, coinInstance); } return this.instanceMap.get(key); } public async getBalance(address: string, chainId: number) { const coinInstance = await this.getInstance(address, chainId); const balance = await coinInstance.methods .balanceOf(AppModule.accountId) .call(); console.log("balance: ", balance); return balance; } /** * get amount of mystery boxes * this method can get amount of general erc721 also * @param {string} address * @param {number} chainId * @return {Promise} */ public async getNftBalance(address: string, chainId: number) { const coinInstance = await this.getInstance( address, chainId, MYSTERY_BOX_ABI ); const balance = await coinInstance.methods .balanceOf(AppModule.accountId) .call(); console.log("nft balance: ", balance); return balance; } /** * Get NFT list of current user * @param {string} address NFT address * @param {number} chainId chain id * @param {number} start * @param {number} page */ public async getNftList( address: string, chainId: number, start = 0, page = 8 ) { const nftInstance = await this.getInstance( address, chainId, MYSTERY_BOX_ABI ); return nftInstance.methods .userTokens(AppModule.accountId, start, page) .call(); } /** * Get NFT of current user with index * @param {string} address * @param {number} chainId * @param {number} index * @return {Promise} */ public async getNftIdOfIndex( address: string, chainId: number, index: number ) { const nftInstance = await this.getInstance( address, chainId, MYSTERY_BOX_ABI ); const nftId = await nftInstance.methods .tokenOfOwnerByIndex(AppModule.accountId, index) .call(); console.log( `address: ${address}, chainId: ${chainId}, index: ${index}, token: ${nftId}` ); return nftId; } /** * Open one mystery box * @param {string} address address of mystery box contract on current chain * @param {string} boxId nftid of mystery box * @param {number[]} tokenIds * @param {string} nonce * @param {string} signature * @return {Promise} */ public async openMysteryBox( address: string, boxId: string, tokenIds: number[], nonce: string, signature: string ) { const proxyInstance = await this.bc.getContractInstance( address, MYSTERY_PROXY_ABI ); // get transactionHash and upload to server for verify return proxyInstance.methods .openBox(boxId, tokenIds, nonce, signature) .send({ gas: 1000000 }); } /** * activate one nft with 18 digital id * @param {string} address * @param {string} nftOld * @param {string} nftNew * @param {number} nftType * @param {string} nonce * @param {string} signature * @return {Promise} */ public async activateOneNft( address: string, nftOld: string, nftNew: string, nftType: number, nonce: string, signature: string ) { const nftProxyInstance = await this.bc.getContractInstance( address, ACTIVATE_PROXY_ABI ); const gas = await nftProxyInstance.methods .activateOne(nftOld, nftNew, nftType, nonce, signature) .estimateGas({ gas: 1000000 }); console.log("nftProxyInstance activateOne need gas: ", gas); return nftProxyInstance.methods .activateOne(nftOld, nftNew, nftType, nonce, signature) .send({ gas: (gas * 1.1) | 0 }); } public async transferToAccount({ to, amount, chainId, address, }: { to: string; amount: number; chainId: number; address: string; }) { const self = this; if (chainId !== this.bc.currentChain) { return new Promise((resolve, reject) => { this.bc.switchEthereumChain(chainId, function () { self.bc .transferToAccount(to, amount, address) .then((res) => { resolve && resolve(res); }) .catch((err) => { reject && reject(err); }); }); }); } else { return this.bc.transferToAccount(to, amount, address); } } }