UNPKG

@broxus/tvm-connect

Version:

TypeScript SDK for connecting to Nekoton-compatible wallets using a unified interface.

345 lines (344 loc) 14.1 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NekotonConnector = void 0; const js_core_1 = require("@broxus/js-core"); const js_utils_1 = require("@broxus/js-utils"); const mobx_1 = require("mobx"); class NekotonConnector extends js_core_1.AbstractStore { params; provider; constructor(params) { super(); this.params = params; (0, mobx_1.makeObservable)(this); } async connect(networkIdOParams, options) { if (!this.provider) { throw new Error('Provider is not defined'); } try { this.setState('isConnecting', true); await this.provider.hasProvider(); let state = await (0, js_utils_1.timeoutPromise)(this.provider.getProviderState(), 2000).catch(() => undefined); if (state?.permissions.accountInteraction) { this.setData({ account: state.permissions.accountInteraction, chainId: state.networkId, version: state.version, }); this.setState({ isConnecting: false, isInitialized: true }); return; } await this.stopSubscriptions({ silent: true }); await this.runSubscriptions(); const request = await this.provider.requestPermissions({ permissions: ['basic', 'accountInteraction'], }); this.setData('account', request.accountInteraction); state = await (0, js_utils_1.timeoutPromise)(this.provider.getProviderState(), 2000); const receivedNetworkId = state.networkId; const desiredNetworkId = typeof networkIdOParams === 'number' ? networkIdOParams : networkIdOParams?.networkId; const name = this.params?.info?.name ?? this.constructor.name ?? this.constructor.prototype.name; if (!desiredNetworkId || receivedNetworkId === desiredNetworkId) { this.setData({ chainId: state.networkId, version: state.version }); this.setState({ isConnecting: false, isInitialized: true }); (0, js_utils_1.debug)(`${name} connection has skipped network switching.`); await options?.onConnect?.(this); return; } if (!networkIdOParams) { (0, js_utils_1.debug)(`${name} connection has skipped network switching. Chain params or chainId is not provided.`); return; } (0, js_utils_1.debug)(`${name} connection trying to switch network.`); await this.switchNetwork(networkIdOParams) .then(async () => { await this.connect(networkIdOParams, options); }) .catch(async () => { await this.connect(undefined, options); }); } catch (e) { this.setState('isConnecting', false); await this.stopSubscriptions(); throw e; } } async disconnect(options) { if ((this.isConnecting || this.isDisconnecting) && !options?.force) { return; } try { this.setState('isDisconnecting', true); await this.provider?.disconnect(); await this.stopSubscriptions(); this.setData(() => ({})); } catch (e) { (0, js_utils_1.groupCollapsed)(`%c${this.constructor.prototype.name || 'Wallet'}%c disconnect failed with an error`, js_core_1.errorLabelStyle, js_core_1.inheritTextStyle); (0, js_utils_1.error)(e); (0, js_utils_1.groupEnd)(); } finally { this.setState('isDisconnecting', false); await options?.onDisconnect?.(this); } } async addAsset(address, type = 'tip3_token') { if (!this.provider) { throw new Error('Provider is not defined'); } if (!this.account?.address) { throw new Error('No connected account'); } const result = await this.provider.addAsset({ account: this.account.address, params: { rootContract: (0, js_core_1.resolveTvmAddress)(address) }, type, }); return result.newAsset; } async addNetwork(network, switchNetwork) { if (!this.provider) { throw new Error('Provider is not defined'); } const result = await this.provider.addNetwork({ network, switchNetwork, }); return result.network; } async switchNetwork(networkIdOParams) { if (!this.provider) { throw new Error('Provider is not defined'); } const networkId = typeof networkIdOParams === 'number' ? networkIdOParams : networkIdOParams.networkId; const result = await this.provider.changeNetwork({ networkId }); if (result.network == null && typeof networkIdOParams !== 'number') { result.network = await this.addNetwork(networkIdOParams, true); } return result.network; } get account() { return this._data.account; } get chainId() { return this._data.chainId; } get version() { return this._data.version; } get isConnecting() { return this._state.isConnecting; } get isDisconnecting() { return this._state.isDisconnecting; } get isInitialized() { return this._state.isInitialized; } get isInitializing() { return this._state.isInitializing; } get info() { return this.params?.info; } async runSubscriptions(options) { const { silent = true } = { ...options }; const [networkChangeSubscription, permissionsSubscription] = await Promise.all([ this.provider?.subscribe('networkChanged'), this.provider?.subscribe('permissionsChanged'), ]); this._networkChangeSubscription = networkChangeSubscription; this._permissionsSubscription = permissionsSubscription; this._networkChangeSubscription?.on('data', (0, js_utils_1.debounce)(this.handleNetworkChanged, 100)); this._permissionsSubscription?.on('data', (0, js_utils_1.debounce)(this.handlePermissionsChanged, 100)); if (!silent) { (0, js_utils_1.debug)('Run subscriptions: network and permissions changes', this.constructor.prototype.name || this.constructor.name); } } async stopSubscriptions(options) { const { silent = true } = { ...options }; await Promise.allSettled([ this._networkChangeSubscription?.unsubscribe(), this._permissionsSubscription?.unsubscribe(), ]); this._networkChangeSubscription = undefined; this._permissionsSubscription = undefined; if (!silent) { (0, js_utils_1.debug)('Subscriptions stopped: network and permissions changes', this.constructor.prototype.name || this.constructor.name); } } async preConnect() { try { if (!this.provider) { return; } this.setState('isConnecting', true); const state = await (0, js_utils_1.timeoutPromise)(this.provider.getProviderState(), 2000); if (!state.permissions.accountInteraction) { (0, js_utils_1.throwException)('No permissions returned'); } this.setData({ account: state.permissions.accountInteraction, chainId: Math.abs(state.networkId), version: state.version, }); await this.params?.onPreconnect?.(this); } catch (e) { (0, js_utils_1.debug)('Venom Wallet could not be pre-connected', e); } finally { this.setState('isConnecting', false); } } async handlePermissionsChanged(event) { const account = event.permissions.accountInteraction; if (!account) { (0, js_utils_1.debug)(`%c${this.constructor.prototype.name || this.constructor.name}%c Permissions have been revoked. Disconnect dApp!`, js_core_1.warningLabelStyle, js_core_1.inheritTextStyle); await this.disconnect({ force: true }); return; } if ((0, js_core_1.isAddressesEquals)(this.account?.address, account.address)) { return; } this.setData('account', account); (0, js_utils_1.debug)(`%c${this.constructor.prototype.name || this.constructor.name}%c Permissions have been changed`, js_core_1.warningLabelStyle, js_core_1.inheritTextStyle); } async handleNetworkChanged(event) { const chainId = event.networkId; if (this.chainId === chainId) { return; } (0, js_utils_1.debug)(`%c${this.constructor.prototype.name || this.constructor.name}%c Network has been changed from ${this.chainId} to ${chainId}`, js_core_1.warningLabelStyle, js_core_1.inheritTextStyle); this.setData('chainId', chainId); } handleDisconnected(err) { this.setData(() => ({})); this.params?.onDisconnect?.(err); } _networkChangeSubscription; _permissionsSubscription; } exports.NekotonConnector = NekotonConnector; __decorate([ mobx_1.observable, __metadata("design:type", Function) ], NekotonConnector.prototype, "provider", void 0); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "connect", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "disconnect", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object, String]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "addAsset", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Boolean]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "addNetwork", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "switchNetwork", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "account", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "chainId", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "version", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "isConnecting", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "isDisconnecting", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "isInitialized", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "isInitializing", null); __decorate([ mobx_1.computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], NekotonConnector.prototype, "info", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "runSubscriptions", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "stopSubscriptions", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "preConnect", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "handlePermissionsChanged", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], NekotonConnector.prototype, "handleNetworkChanged", null); __decorate([ mobx_1.action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Error]), __metadata("design:returntype", void 0) ], NekotonConnector.prototype, "handleDisconnected", null);