@broxus/tvm-connect
Version:
TypeScript SDK for connecting to Nekoton-compatible wallets using a unified interface.
345 lines (344 loc) • 14.1 kB
JavaScript
"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);