import { Chain } from '../chain/Chain' import { AVAILABLE_CHAINS, IChainData } from '../configs/config_chain' import { AllChains } from '../configs/allchain' import { ACTIVATE_PROXY_ABI, MYSTERY_BOX_ABI, MYSTERY_PROXY_ABI, } from '../configs/contracts' import { singleton } from '../decorator/singleton.decorator' import { Blockchain } from './blockchain' import { UserModule } from '../module/UserModule' import { getNonce } from '../api/User' import { EventBus, SHOW_ERR_MSG } from '../utils/event-bus' @singleton 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() await this.getNance() } catch (err) { console.log('connect chain error: ', err) } } } get isLogined() { return !!UserModule.token && !!UserModule.step } public async logout() { await this.bc.disconnect() } public get currentChain() { return this.bc.currentChain } public async login() { if (!UserModule.step) { try { await this.bc.connect(true) await this.checkNance() } catch (err) { EventBus.$emit(SHOW_ERR_MSG, err.message) await Promise.reject(err) } } } public async checkNance() { try { let nonce = UserModule.nonce if (!nonce) { const preRequest: any = await getNonce({ account: UserModule.accountId, net_id: UserModule.chainId, }) nonce = preRequest.nonce + '' UserModule.updateNonce(nonce) } await UserModule.Login({ bcInstance: this.bc, account: UserModule.accountId, chainId: UserModule.chainId, nonce, }) UserModule.updateStep(1) } catch (err) { console.log(err) await Promise.reject(err) } } public async getNance() { console.log('need get nance') try { const preRequest: any = await getNonce({ account: UserModule.accountId, net_id: UserModule.chainId, }) console.log('success get nonce: ', preRequest) // if need check sign and has nonce val, store it if (preRequest.state) { UserModule.updateStep(1) } else if (!preRequest.state && preRequest.nonce) { if (UserModule.token) { await UserModule.LogOut() } UserModule.updateStep(0) } if (preRequest.nonce) { UserModule.updateNonce(preRequest.nonce + '') } } catch (err) { EventBus.$emit(SHOW_ERR_MSG, 'Error get login nonce') } } 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(UserModule.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(UserModule.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(UserModule.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(UserModule.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) } } }