UNPKG

@cosmos-kit/core

Version:

cosmos-kit wallet connector core package

312 lines (311 loc) 12.5 kB
import { COSMIFRAME_WALLET_ID } from '../cosmiframe/constants'; import { State, } from '../types'; import { ConnectError } from '../utils'; import { WalletBase } from './wallet'; export class ChainWalletBase extends WalletBase { mainWallet; chainRecord; _rpcEndpoint; _restEndpoint; connectChains; offlineSigner; namespace = 'cosmos'; preferredSignType; constructor(walletInfo, chainRecord) { super(walletInfo); this.chainRecord = chainRecord; this.preferredSignType = chainRecord.clientOptions?.preferredSignType || 'amino'; } get chain() { return this.chainRecord.chain; } get assetList() { return this.chainRecord.assetList; } get isTestNet() { return this.chainName.includes('testnet'); } get preferredEndpoints() { return this.chainRecord.preferredEndpoints; } get rpcEndpoints() { return this.preferredEndpoints?.rpc || []; } get restEndpoints() { return this.preferredEndpoints?.rest || []; } /** * stands for real `chainIsLazy` considered both `globalIsLazy` and `chainIsLazy` settings */ get isLazy() { return this.preferredEndpoints.isLazy; } addEndpoints(endpoints) { this.chainRecord.preferredEndpoints = { isLazy: endpoints?.isLazy ?? this.preferredEndpoints?.isLazy, rpc: [...(endpoints?.rpc || []), ...(this.preferredEndpoints?.rpc || [])], rest: [ ...(endpoints?.rest || []), ...(this.preferredEndpoints?.rest || []), ], }; } get chainName() { return this.chainRecord.name; } get chainLogoUrl() { return ( // until chain_registry fix this // this.chainInfo.chain.logo_URIs?.svg || // this.chainInfo.chain.logo_URIs?.png || // this.chainInfo.chain.logo_URIs?.jpeg || this.assetList?.assets[0]?.logo_URIs?.svg || this.assetList?.assets[0]?.logo_URIs?.png || undefined); } get stargateOptions() { return this.chainRecord.clientOptions?.stargate; } get signingStargateOptions() { return (this.chainRecord.clientOptions?.signingStargate || this.chainRecord.clientOptions?.stargate); } get signingCosmwasmOptions() { return this.chainRecord.clientOptions?.signingCosmwasm; } get assets() { return this.assetList?.assets; } get chainId() { return this.chain?.chain_id; } get cosmwasmEnabled() { return this.chain?.codebase?.cosmwasm_enabled; } get username() { return this.data?.username; } get address() { return this.data?.address; } setData(data) { this.logger?.debug(`[Data Change] ${JSON.stringify(this.data)} -> ${JSON.stringify(data)} (${this.chainName}/${this.walletName})`); this._mutable.data = data; this.actions?.data?.(data); const accountsStr = window.localStorage.getItem('cosmos-kit@2:core//accounts'); let accounts = accountsStr ? JSON.parse(accountsStr) : []; if (typeof data === 'undefined') { accounts = accounts.filter((a) => a.chainId !== this.chainId || a.namespace !== this.namespace); } else { accounts = accounts.filter((a) => a.chainId !== this.chainId || a.namespace !== this.namespace); accounts.push(data); } window?.localStorage.setItem('cosmos-kit@2:core//accounts', JSON.stringify(accounts)); this.session?.update(); } initClient(_options) { throw new Error('initClient not implemented'); } async update(options = { connect: true }) { this.setState(State.Pending); this.setMessage(void 0); try { if (options.connect) { this.connectChains ? await this.connectChains() : await this?.client?.connect?.(this.chainId); } let account; try { this.logger?.debug(`Fetching ${this.walletName} ${this.chainId} account.`); account = await this?.client?.getSimpleAccount(this.chainId); } catch (error) { if (this.rejectMatched(error)) { this.logger?.debug(`Fetching rejected.`); this.setRejected(new ConnectError()); return; } if (this.client && this?.client?.addChain) { this.logger?.debug(`Going to add chain ${this.chainId}`); await this.client.addChain(this.chainRecord); account = await this.client.getSimpleAccount(this.chainId); } else { throw error; } } this.setData(account); this.setState(State.Done); this.setMessage(void 0); } catch (e) { // this.logger?.error(e); if (e && this.rejectMatched(e)) { this.setRejected(new ConnectError()); } else { this.setError(new ConnectError(e.message)); } } if (!this.isWalletRejected && this.walletName !== COSMIFRAME_WALLET_ID) { window?.localStorage.setItem('cosmos-kit@2:core//current-wallet', this.walletName); } } getRpcEndpoint = async (isLazy) => { const { getIsLazy } = await import('../utils'); const lazy = getIsLazy(void 0, this.isLazy, this._rpcEndpoint?.isLazy, isLazy, this.logger); if (lazy) { const endpoint = this._rpcEndpoint || this.rpcEndpoints[0]; if (!endpoint) { return Promise.reject('No endpoint available.'); } return endpoint; } const nodeType = 'rpc'; const { isValidEndpoint } = await import('../utils'); if (this._rpcEndpoint && (await isValidEndpoint(this._rpcEndpoint, nodeType, lazy, this.logger))) { return this._rpcEndpoint; } const { getFastestEndpoint } = await import('../utils'); this._rpcEndpoint = await getFastestEndpoint(this.rpcEndpoints, nodeType, this.logger); return this._rpcEndpoint; }; getRestEndpoint = async (isLazy) => { const { getIsLazy } = await import('../utils'); const lazy = getIsLazy(void 0, this.isLazy, this._restEndpoint?.isLazy, isLazy, this.logger); if (lazy) { const endpoint = this._restEndpoint || this.restEndpoints[0]; if (!endpoint) { return Promise.reject('No endpoint available.'); } return endpoint; } const nodeType = 'rest'; const { isValidEndpoint } = await import('../utils'); if (this._restEndpoint && (await isValidEndpoint(this._restEndpoint, nodeType, lazy, this.logger))) { return this._restEndpoint; } const { getFastestEndpoint } = await import('../utils'); this._restEndpoint = await getFastestEndpoint(this.restEndpoints, nodeType, this.logger); return this._restEndpoint; }; getStargateClient = async () => { const rpcEndpoint = await this.getRpcEndpoint(); const { StargateClient } = await import('@cosmjs/stargate'); return StargateClient.connect(rpcEndpoint, this.stargateOptions); }; getCosmWasmClient = async () => { const rpcEndpoint = await this.getRpcEndpoint(); const { CosmWasmClient } = await import('@cosmjs/cosmwasm-stargate'); return CosmWasmClient.connect(rpcEndpoint); }; getNameService = async () => { const client = await this.getCosmWasmClient(); const { getNameServiceRegistryFromChainName } = await import('../utils'); const registry = getNameServiceRegistryFromChainName(this.chainName); const { NameService } = await import('../name-service'); return new NameService(client, registry); }; async initOfflineSigner(preferredSignType) { if (typeof this.client === 'undefined') { throw new Error('WalletClient is not initialized'); } if (preferredSignType) this.preferredSignType = preferredSignType; this.offlineSigner = await this.client.getOfflineSigner(this.chainId, this.preferredSignType); } getSigningStargateClient = async () => { const rpcEndpoint = await this.getRpcEndpoint(); if (!this.offlineSigner) { await this.initOfflineSigner(); } const { SigningStargateClient } = await import('@cosmjs/stargate'); return SigningStargateClient.connectWithSigner(rpcEndpoint, this.offlineSigner, this.signingStargateOptions); }; getSigningCosmWasmClient = async () => { const rpcEndpoint = await this.getRpcEndpoint(); if (!this.offlineSigner) { await this.initOfflineSigner(); } const { SigningCosmWasmClient } = await import('@cosmjs/cosmwasm-stargate'); return SigningCosmWasmClient.connectWithSigner(rpcEndpoint, this.offlineSigner, this.signingCosmwasmOptions); }; getSigningClient = async (type) => { switch (type) { case 'stargate': return await this.getSigningStargateClient(); case 'cosmwasm': return await this.getSigningCosmWasmClient(); default: return this.getSigningStargateClient(); } }; estimateFee = async (messages, type, memo, multiplier) => { if (!this.address) { throw new Error('Address is required to estimate fee. Try connect to fetch address.'); } let gasPrice; switch (type) { case 'stargate': gasPrice = this.signingStargateOptions?.gasPrice; break; case 'cosmwasm': gasPrice = this.signingCosmwasmOptions?.gasPrice; break; default: gasPrice = this.signingStargateOptions?.gasPrice; break; } if (!gasPrice) { throw new Error('Gas price must be set in the client options when auto gas is used.'); } const client = await this.getSigningClient(type); const gasEstimation = await client.simulate(this.address, messages, memo); const { calculateFee } = await import('@cosmjs/stargate'); return calculateFee(Math.round(gasEstimation * (multiplier || 1.4)), gasPrice); }; sign = async (messages, fee, memo, type) => { if (!this.address) { throw new Error('Address is required to estimate fee. Try connect to fetch address.'); } const client = await this.getSigningClient(type); let usedFee; if (typeof fee === 'undefined' || typeof fee === 'number') { usedFee = await this.estimateFee(messages, type, memo, fee); } else { usedFee = fee; } return await client.sign(this.address, messages, usedFee, memo || ''); }; broadcast = async (signedMessages, type) => { const client = await this.getSigningClient(type); const { TxRaw } = await import('cosmjs-types/cosmos/tx/v1beta1/tx'); const txBytes = TxRaw.encode(signedMessages).finish(); let timeoutMs, pollIntervalMs; switch (type) { case 'stargate': timeoutMs = this.signingStargateOptions?.broadcastTimeoutMs; pollIntervalMs = this.signingStargateOptions?.broadcastPollIntervalMs; break; case 'cosmwasm': timeoutMs = this.signingCosmwasmOptions?.broadcastTimeoutMs; pollIntervalMs = this.signingCosmwasmOptions?.broadcastPollIntervalMs; break; default: timeoutMs = this.signingStargateOptions?.broadcastTimeoutMs; pollIntervalMs = this.signingStargateOptions?.broadcastPollIntervalMs; break; } return client.broadcastTx(txBytes, timeoutMs, pollIntervalMs); }; signAndBroadcast = async (messages, fee, memo, type) => { const signedMessages = await this.sign(messages, fee, memo, type); return this.broadcast(signedMessages, type); }; }