UNPKG

@amfi/connect-wallet

Version:

Connect Crypto Wallet by using CoinbaseWallet (WalletLink), KardiaChain, MetaMask browser extentions or mobile application and WalletConnect service by scanning Wallet Qr-code.

401 lines (366 loc) 13.3 kB
import Web3 from 'web3'; import { Observable } from 'rxjs'; import { Contract } from 'web3-eth-contract'; import { provider } from 'web3-core'; import { MetamaskConnect } from './metamask'; import { WalletsConnect } from './wallet-connect'; import { CoinbaseWalletConnect } from './coinbase-wallet'; import { KardiaChainConnect } from './kardiachain'; import { INetwork, IMessageProvider, IContract, IProvider, IAddContract, IConnect, ISettings, IError, IConnectorMessage, ContractWeb3, IChain, INoNameContract, IEvent, IEventError, } from './interface'; import { parameters, addChains } from './helpers'; export class ConnectWallet { private connector: | MetamaskConnect | WalletsConnect | CoinbaseWalletConnect | KardiaChainConnect; private providerName: string; private availableProviders: string[] = [ 'MetaMask', 'WalletConnect', 'CoinbaseWallet', 'KardiaChain', ]; private network: INetwork; private settings: ISettings; public Web3: Web3; private contracts: IContract = {}; private allTxSubscribers = []; /** * Connect provider to web3 and get access to web3 methods, account address and transaction in blockchain. * Supported MetaMask, WalletConnect, Kardiachain and CoinBase providers. */ constructor(initProvider?: provider) { if (initProvider) { this.Web3 = new Web3(initProvider); } } /** * Add custom chains to Connect Wallet, provide an array of chains than return chain list parameters. * * @returns return chains list parameters * @example this.addChains([chain,chain]).then((parameters: any) => console.log(parameters),(err) => console.log(err)); */ public addChains = (chains: IChain[]): any => addChains(chains); /** * Create new wallet provider with network and settings valuse by passing it in function arguments. * * @param {IProvider} provider provider data with provider name and setting. * @param {INetwork} network application working network name and chainID. * @param {ISettings} settings connect wallet settings. * @returns return connection info in boolean (true - connected, false - not connected) or error provider. * @example connectWallet.connect(providerWallet, networkWallet, connectSetting).then((connect) => * {console.log(connect);},(error) => {console.log('connect error', error);}); */ public async connect( provider: IProvider, network: INetwork, settings?: ISettings, ): Promise<IConnectorMessage> { if (!this.availableProviders.includes(provider.name)) { return { code: 2, type: 'error', connected: false, provider, message: { title: 'Error', subtitle: 'Provider Error', text: `Your provider doesn't exists`, }, }; } this.network = network; this.settings = settings ? settings : { providerType: false }; this.connector = this.chooseProvider(provider.name); return new Promise<IConnectorMessage>((resolve, reject) => { this.connector .connect(provider) .then((connect) => this.applySettings(connect)) .then((connect: IConnectorMessage) => { connect.connected ? this.initWeb3(connect.provider) : reject(connect); resolve(connect); },(err) => reject(this.applySettings(err))); }); } /** * Find and choose available provider for create connection. * * @param {Srtring} name provider name passing from connect function in provider value. * @returns return selected provider class. * @example connectWallet.chooseProvider('MetaMask'); //=> new MetamaskConnect() */ private chooseProvider(name: string): MetamaskConnect | WalletsConnect { this.providerName = name; switch (name) { case 'MetaMask': return new MetamaskConnect(this.network); case 'WalletConnect': return new WalletsConnect(); case 'CoinbaseWallet': return new CoinbaseWalletConnect(this.network); case 'KardiaChain': return new KardiaChainConnect(); } } /** * Initialize a Web3 by set provider after using connect function. * * @param {Any} provider array with provider information. * @example connectWallet.initWeb3(provider); */ private initWeb3(provider: any): void { if (this.Web3) { this.Web3.setProvider(provider); } else { this.Web3 = new Web3(provider); } } /** * Geting current connectror * * @example connectWallet.getConnector(); */ public getConnector(): | MetamaskConnect | WalletsConnect | CoinbaseWalletConnect | KardiaChainConnect { return this.connector; } /** * Geting current providerName * * @example connectWallet.getproviderName(); */ public getproviderName(): string { return this.providerName; } /** * Add settings parameters to connect wallet answers. * * @param {Array} data array which needs to apply a settings parameters * @returns return completedata with settings parameters * @example connectWallet.applySettings(data); //=> data.type etc. */ private applySettings( data: IConnectorMessage | IError | IConnect, ): IConnectorMessage | IError | IConnect { if (this.settings.providerType) { data.type = this.providerName; } return data; } /** * Get account address and chain information from selected wallet provider. * * @returns return an Promise array with data error or connected. * @example connectWallet.getAccounts().then((account: any)=> console.log('account',account),(err: any => console.log('account', err))); */ public getAccounts(): Promise<IConnect | IError | { address: string }> { const error: IError = { code: 4, message: { title: 'Error', subtitle: 'Chain error', text: '', }, }; return new Promise((resolve, reject) => { if (this.currentWeb3() && !this.connector) { const { address, accounts } = this.currentWeb3().currentProvider as any; resolve({ address: address || accounts[0] }); } else if (this.connector) { const { chainID } = this.network; const { chainsMap, chainIDMap } = parameters; this.connector.getAccounts().then( (connectInfo: IConnect) => { if (connectInfo.network.chainID !== chainID) { error.message.text = `Please set network: ${ chainsMap[chainIDMap[chainID]].name }.`; reject(this.applySettings(error)); } else { resolve(this.applySettings(connectInfo)); } }, (error: IError) => { reject(this.applySettings(error)); }, ); } else { error.code = 7; error.message = null; reject(this.applySettings(error)); } }); } /** * Create new Observer of transactions and add it to array of all transaction subscribers * variable. You can subscribe to waiting answer from blockchain if your trunsaction * finshed success or not. You will get transaction hash. * * @returns return new observable value. * @example connectWallet.txSubscribe().subscribe((tx) => {console.log('transacton', tx)}); */ public txSubscribe(): Observable<any> { const newObserver = new Observable((observer) => { this.allTxSubscribers.push(observer); }); return newObserver; } /** * Trigger all transaction subscribers and pass transaction hash if transaction complete. * * @param {String} txHash transaction hash of success transaction. * @example connectWallet.clTxSubscribers(txHash); */ public clTxSubscribers(txHash: string): void { this.allTxSubscribers.forEach((observer) => { observer.next(txHash); }); } /** * Checking transaction hash status in blockchain. And return transacrion hash if transaction * was found and success or reject null if dont have enouth data or information. * * @param {String} txHash transaction hash. * @param {Any} resolve resolve transaction hash. * @param {Any} reject reject if transaction not found. * @returns return transaction hash or reject with null. * @example new Promise((resolve, reject) => {connectWallet.checkTx(txHash, resolve, reject);}); */ private txStatus(txHash: string, resolve: any, reject: any): void { this.Web3.eth.getTransactionReceipt(txHash, (err: any, res: any) => { if (err || (res && res.blockNumber && !res.status)) { reject(err); } else if (res && res.blockNumber) { this.clTxSubscribers(txHash); resolve(res); } else if (!res) { setTimeout(() => { this.txStatus(txHash, resolve, reject); }, 2000); } }); } /** * Transaction check in blockchain. Use this funnction to start check transaction by his hash. * This function will triggered all transaction subscribers when transaction complete successful or * with errors. You need to provide transaction hash in function after you approve it. * * @param {String} txHash transaction hash. * @returns return promise with transaction search info, can return transaction hash or null. * @example connectWallet.txCheck(txHash).then((txHash: string) => console.log(txHash),(err) => console.log(err)); */ public txCheck(txHash: string): Promise<any> { return new Promise((resolve, reject) => { this.txStatus(txHash, resolve, reject); }); } /** * Add contract to Web3 without providing contract name to initialize it, then you will * able to use contract function to get contract from web3 and use contract methods. * * @param {INoNameContract} contract contract object with contract address and abi. * @returns return contract web3 methods. * @example connectWallet.getContract(contract); */ public getContract(contract: INoNameContract): Contract { return new this.Web3.eth.Contract(contract.abi, contract.address); } /** * Add contract to Web3. Provide contract name, address and abi code to initialize it, then you will * able to use contract(name) function to get contract from web3 and use contract methods. * * @param {IAddContract} contract contract object with contract name, address and abi. * @returns return true if contact added successfully or false if have some errors. * @example connectWallet.addContract(contract).then((contractStatus: boolean) => console.log(contractStatus), (err) => console.log(err)); */ public addContract(contract: IAddContract): Promise<boolean> { return new Promise<any>((resolve, reject) => { try { this.contracts[contract.name] = new this.Web3.eth.Contract( contract.abi, contract.address, ); resolve(true); } catch { reject(false); } }); } /** * Get contract by providing contract name. If you don't have contracts use addContract function to initialize it. * * @param {String} name contract name. * @returns return contract parameters and methods. * @example connectWallet.Contract(ContractName); */ public Contract = (name: string): ContractWeb3 => this.contracts[name]; /** * Get access to use Web3. Return Web3 variable to use it methods and parameters. * * @returns return Web3 * @example connectWallet.currentWeb3(); */ public currentWeb3 = (): Web3 => this.Web3; /** * Get account balance from ethereum blockchain. Provide address in function arguments to recive address balance * from blockchain. * * @param {String} address address. * @returns return address balance. * @example connectWallet.getBalance(address).then((balance: string)=> {console.log(balance)}); */ public getBalance = (address: string): Promise<string | number> => { return this.Web3.eth.getBalance(address); }; /** * Logout function. Use this function if you want to do logout from your application. Function will reset * current connection to defoult then you need to initialize connect() function again to connect to your * provider. * * @example connectWallet.resetConect(); */ public resetConect = (): void => (this.connector = undefined); /** * Use this method to sign custom mesaage. * * @example connectWallet.signMsg('0x0000000000000000000', 'some_data').then(data => console.log('sign:', data),err => console.log('sign err:',err)); */ public signMsg = (userAddr: string, msg: string): Promise<any> => { return this.Web3.eth.personal.sign(msg, userAddr, ''); }; /** * Subscribe to events from current connection: connect, disconnect, chain change, account change and etc. * * @example connectWallet.eventSubscriber().subscribe((event:IEvent) => console.log('event from subscribe', event), (err:IEventError) => console.log('event error', err)); */ public eventSubscriber(): Observable<IEvent | IEventError> { if (!this.connector) { throw new Error("connector haven't initialized"); } return new Observable((observer) => { this.connector.eventSubscriber().subscribe( (event: IEvent) => observer.next(event), (error: IEventError) => observer.error(error), ); }); } }