From bd703ad662eccfee3943c10210bc8382d9c9b397 Mon Sep 17 00:00:00 2001 From: Alejandbel Date: Fri, 28 Nov 2025 14:15:44 +0300 Subject: [PATCH] feature: actualize wallet bridges --- .../src/provider/bridge/bridge-provider.ts | 17 +++----- .../provider/injected/injected-provider.ts | 11 ++---- .../wallet-connect/wallet-connect-provider.ts | 11 ++---- .../src/storage/bridge-connection-storage.ts | 39 +++++++++++++++++-- packages/sdk/src/ton-connect.ts | 21 ++++++---- packages/sdk/src/wallets-list-manager.ts | 17 ++++++++ 6 files changed, 79 insertions(+), 37 deletions(-) diff --git a/packages/sdk/src/provider/bridge/bridge-provider.ts b/packages/sdk/src/provider/bridge/bridge-provider.ts index b949d0a23..d20b1faf1 100644 --- a/packages/sdk/src/provider/bridge/bridge-provider.ts +++ b/packages/sdk/src/provider/bridge/bridge-provider.ts @@ -21,7 +21,6 @@ import { BridgeIncomingMessage } from 'src/provider/bridge/models/bridge-incommi import { BridgePartialSession, BridgeSession } from 'src/provider/bridge/models/bridge-session'; import { HTTPProvider } from 'src/provider/provider'; import { BridgeConnectionStorage } from 'src/storage/bridge-connection-storage'; -import { IStorage } from 'src/storage/models/storage.interface'; import { Optional, OptionalTraceable, Traceable, WithoutId } from 'src/utils/types'; import { PROTOCOL_VERSION } from 'src/resources/protocol'; import { logDebug, logError } from 'src/utils/log'; @@ -36,11 +35,10 @@ import { UUIDv7 } from 'src/utils/uuid'; export class BridgeProvider implements HTTPProvider { public static async fromStorage( - storage: IStorage, + storage: BridgeConnectionStorage, analyticsManager?: AnalyticsManager ): Promise { - const bridgeConnectionStorage = new BridgeConnectionStorage(storage); - const connection = await bridgeConnectionStorage.getHttpConnection(); + const connection = await storage.getHttpConnection(); if (isPendingConnectionHttp(connection)) { return new BridgeProvider(storage, connection.connectionSource, analyticsManager); @@ -56,8 +54,6 @@ export class BridgeProvider implements HTTPProvider { private readonly standardUniversalLink = 'tc://'; - private readonly connectionStorage: BridgeConnectionStorage; - private readonly pendingRequests = new Map< string, (response: TraceableWalletResponse) => void @@ -80,13 +76,12 @@ export class BridgeProvider implements HTTPProvider { private readonly analytics?: Analytics; constructor( - private readonly storage: IStorage, + private readonly connectionStorage: BridgeConnectionStorage, private readonly walletConnectionSource: | Optional | Pick[], private readonly analyticsManager?: AnalyticsManager ) { - this.connectionStorage = new BridgeConnectionStorage(storage); this.analytics = this.analyticsManager?.scoped(); } @@ -208,7 +203,7 @@ export class BridgeProvider implements HTTPProvider { } this.gateway = new BridgeGateway( - this.storage, + this.connectionStorage.storage, this.walletConnectionSource.bridgeUrl, storedConnection.session.sessionCrypto.sessionId, this.gatewayListener.bind(this), @@ -603,7 +598,7 @@ export class BridgeProvider implements HTTPProvider { // open new gateways this.pendingGateways = this.walletConnectionSource.map(source => { const gateway = new BridgeGateway( - this.storage, + this.connectionStorage.storage, source.bridgeUrl, sessionCrypto.sessionId, () => {}, @@ -650,7 +645,7 @@ export class BridgeProvider implements HTTPProvider { } this.gateway = new BridgeGateway( - this.storage, + this.connectionStorage.storage, this.walletConnectionSource.bridgeUrl, sessionCrypto.sessionId, this.gatewayListener.bind(this), diff --git a/packages/sdk/src/provider/injected/injected-provider.ts b/packages/sdk/src/provider/injected/injected-provider.ts index 7c1ba6b6f..4b5a13609 100644 --- a/packages/sdk/src/provider/injected/injected-provider.ts +++ b/packages/sdk/src/provider/injected/injected-provider.ts @@ -12,7 +12,6 @@ import { } from 'src/provider/injected/models/injected-wallet-api'; import { InternalProvider } from 'src/provider/provider'; import { BridgeConnectionStorage } from 'src/storage/bridge-connection-storage'; -import { IStorage } from 'src/storage/models/storage.interface'; import { OptionalTraceable, Traceable, WithoutId } from 'src/utils/types'; import { getWindow, getWindowEntries } from 'src/utils/web-api'; import { PROTOCOL_VERSION } from 'src/resources/protocol'; @@ -34,11 +33,10 @@ export class InjectedProvider implements InternalProv private static window = getWindow(); public static async fromStorage( - storage: IStorage, + storage: BridgeConnectionStorage, analyticsManager?: AnalyticsManager ): Promise { - const bridgeConnectionStorage = new BridgeConnectionStorage(storage); - const connection = await bridgeConnectionStorage.getInjectedConnection(); + const connection = await storage.getInjectedConnection(); return new InjectedProvider(storage, connection.jsBridgeKey, analyticsManager); } @@ -97,8 +95,6 @@ export class InjectedProvider implements InternalProv private injectedWallet: InjectedWalletApi; - private readonly connectionStorage: BridgeConnectionStorage; - private listenSubscriptions = false; private listeners: Array<(e: TraceableWalletEvent) => void> = []; @@ -108,7 +104,7 @@ export class InjectedProvider implements InternalProv >; constructor( - storage: IStorage, + private readonly connectionStorage: BridgeConnectionStorage, private readonly injectedWalletKey: T, analyticsManager?: AnalyticsManager ) { @@ -117,7 +113,6 @@ export class InjectedProvider implements InternalProv throw new WalletNotInjectedError(); } - this.connectionStorage = new BridgeConnectionStorage(storage); this.injectedWallet = window[injectedWalletKey]!.tonconnect!; if (analyticsManager) { diff --git a/packages/sdk/src/provider/wallet-connect/wallet-connect-provider.ts b/packages/sdk/src/provider/wallet-connect/wallet-connect-provider.ts index dc43e1c80..654070d2f 100644 --- a/packages/sdk/src/provider/wallet-connect/wallet-connect-provider.ts +++ b/packages/sdk/src/provider/wallet-connect/wallet-connect-provider.ts @@ -19,7 +19,6 @@ import { TraceableWalletEvent, TraceableWalletResponse } from 'src/models/wallet import { OptionalTraceable, Traceable, WithoutId } from 'src/utils/types'; import { UUIDv7 } from 'src/utils/uuid'; import { InternalProvider } from 'src/provider/provider'; -import { IStorage } from 'src/storage/models/storage.interface'; import { BridgeConnectionStorage } from 'src/storage/bridge-connection-storage'; import { TonConnectError } from 'src/errors'; import { @@ -50,8 +49,6 @@ export class WalletConnectProvider implements InternalProvider { private abortController?: AbortController; - private readonly connectionStorage: BridgeConnectionStorage; - private readonly config: { projectId: string; metadata: WalletConnectMetadata; @@ -70,9 +67,7 @@ export class WalletConnectProvider implements InternalProvider { }[]; }; - constructor(storage: IStorage) { - this.connectionStorage = new BridgeConnectionStorage(storage); - + constructor(private readonly connectionStorage: BridgeConnectionStorage) { const { projectId, metadata } = getWalletConnectOptions(); this.config = { @@ -106,7 +101,9 @@ export class WalletConnectProvider implements InternalProvider { }; } - public static async fromStorage(storage: IStorage): Promise { + public static async fromStorage( + storage: BridgeConnectionStorage + ): Promise { return new WalletConnectProvider(storage); } diff --git a/packages/sdk/src/storage/bridge-connection-storage.ts b/packages/sdk/src/storage/bridge-connection-storage.ts index 273666d50..cf67bc71b 100644 --- a/packages/sdk/src/storage/bridge-connection-storage.ts +++ b/packages/sdk/src/storage/bridge-connection-storage.ts @@ -15,11 +15,16 @@ import { isPendingConnectionHttpRaw } from 'src/provider/bridge/models/bridge-connection'; import { IStorage } from 'src/storage/models/storage.interface'; +import { logDebug } from 'src/utils/log'; +import { WalletsListManager } from 'src/wallets-list-manager'; export class BridgeConnectionStorage { private readonly storeKey = 'ton-connect-storage_bridge-connection'; - constructor(private readonly storage: IStorage) {} + constructor( + public readonly storage: IStorage, + private readonly walletsListManager: WalletsListManager + ) {} public async storeConnection(connection: BridgeConnection): Promise { if (connection.type === 'injected' || connection.type === 'wallet-connect') { @@ -71,7 +76,7 @@ export class BridgeConnectionStorage { if (!isPendingConnectionHttpRaw(connection)) { const sessionCrypto = new SessionCrypto(connection.session.sessionKeyPair); - return { + return this.actualizeBridgeConnection({ type: 'http', connectEvent: connection.connectEvent, lastWalletEventId: connection.lastWalletEventId, @@ -81,7 +86,7 @@ export class BridgeConnectionStorage { bridgeUrl: connection.session.bridgeUrl, walletPublicKey: connection.session.walletPublicKey } - }; + }); } if (isExpiredPendingConnectionHttpRaw(connection)) { @@ -214,4 +219,32 @@ export class BridgeConnectionStorage { return 0; } + + private async actualizeBridgeConnection( + connection: BridgeConnectionHttp + ): Promise { + try { + const appName = connection.connectEvent.payload.device.appName; + const wallet = await this.walletsListManager.getRemoteWallet(appName); + + if (wallet.bridgeUrl === connection.session.bridgeUrl) { + return connection; + } + + const actualizedConnection = { + ...connection, + session: { + ...connection.session, + bridgeUrl: wallet.bridgeUrl + } + } satisfies BridgeConnectionHttp; + + await this.storeConnection(connection); + + return actualizedConnection; + } catch (error) { + logDebug('Failed to actualize bridge connection', error); + return connection; + } + } } diff --git a/packages/sdk/src/ton-connect.ts b/packages/sdk/src/ton-connect.ts index 8979383be..f048fd580 100644 --- a/packages/sdk/src/ton-connect.ts +++ b/packages/sdk/src/ton-connect.ts @@ -112,7 +112,7 @@ export class TonConnect implements ITonConnect { */ private readonly tracker: TonConnectTracker; - private readonly walletsList = new WalletsListManager(); + private readonly walletsList: WalletsListManager; private readonly analytics?: AnalyticsManager; @@ -205,7 +205,10 @@ export class TonConnect implements ITonConnect { ); } - this.bridgeConnectionStorage = new BridgeConnectionStorage(this.dappSettings.storage); + this.bridgeConnectionStorage = new BridgeConnectionStorage( + this.dappSettings.storage, + this.walletsList + ); if (!options?.disableAutoPauseConnection) { this.addWindowFocusAndBlurSubscriptions(); @@ -408,18 +411,20 @@ export class TonConnect implements ITonConnect { switch (bridgeConnectionType) { case 'http': provider = await BridgeProvider.fromStorage( - this.dappSettings.storage, + this.bridgeConnectionStorage, this.analytics ); break; case 'injected': provider = await InjectedProvider.fromStorage( - this.dappSettings.storage, + this.bridgeConnectionStorage, this.analytics ); break; case 'wallet-connect': - provider = await WalletConnectProvider.fromStorage(this.dappSettings.storage); + provider = await WalletConnectProvider.fromStorage( + this.bridgeConnectionStorage + ); break; default: if (embeddedWallet) { @@ -848,14 +853,14 @@ export class TonConnect implements ITonConnect { if (!Array.isArray(wallet) && isWalletConnectionSourceJS(wallet)) { provider = new InjectedProvider( - this.dappSettings.storage, + this.bridgeConnectionStorage, wallet.jsBridgeKey, this.analytics ); } else if (!Array.isArray(wallet) && isWalletConnectionSourceWalletConnect(wallet)) { - provider = new WalletConnectProvider(this.dappSettings.storage); + provider = new WalletConnectProvider(this.bridgeConnectionStorage); } else { - provider = new BridgeProvider(this.dappSettings.storage, wallet, this.analytics); + provider = new BridgeProvider(this.bridgeConnectionStorage, wallet, this.analytics); } provider.listen(this.walletEventsListener.bind(this)); diff --git a/packages/sdk/src/wallets-list-manager.ts b/packages/sdk/src/wallets-list-manager.ts index fe26a968f..d0ff5f105 100644 --- a/packages/sdk/src/wallets-list-manager.ts +++ b/packages/sdk/src/wallets-list-manager.ts @@ -13,6 +13,7 @@ import { InjectedProvider } from 'src/provider/injected/injected-provider'; import { logError } from 'src/utils/log'; import { FALLBACK_WALLETS_LIST } from 'src/resources/fallback-wallets-list'; import { isQaModeEnabled } from './utils/qa-mode'; +import { TonConnectError } from 'src/errors'; export class WalletsListManager { private walletsListDTOCache: Promise | null = null; @@ -77,6 +78,22 @@ export class WalletsListManager { return this.walletsListDTOCache; } + public async getRemoteWallet(appName: string): Promise { + const walletsList = await this.getWallets(); + + const wallet = walletsList.find(wallet => wallet.appName === appName); + + if (!wallet) { + throw new TonConnectError(`Wallet info not found for appName: "${appName}"`); + } + + if (!('bridgeUrl' in wallet)) { + throw new TonConnectError(`Wallet "${appName}" is not remote`); + } + + return wallet; + } + private async fetchWalletsListFromSource(): Promise { let walletsList: WalletInfoDTO[] = [];