Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { TONConnectSessionManager, TONConnectSession, DAppInfo, Wallet, WalletId } from '@ton/walletkit';

/**
* Swift adapter for TONConnect session management.
* Delegates all session operations to the Swift implementation.
*/
export class SwiftTONConnectSessionsManager implements TONConnectSessionManager {
private swiftSessionsManager: TONConnectSessionManager;

constructor(swiftSessionsManager: TONConnectSessionManager) {
this.swiftSessionsManager = swiftSessionsManager;
}

async initialize(): Promise<void> {
/*
No initialization needed for Swift implementation.
This method needed to comply with the TONConnectSessionManager interface.
Such compliance is needed for backward compability with existing codebase.
*/
}

async createSession(
sessionId: string,
dAppInfo: DAppInfo,
wallet: Wallet,
isJsBridge: boolean,
): Promise<TONConnectSession> {
return await this.swiftSessionsManager.createSession(sessionId, dAppInfo, wallet, isJsBridge);
}

async getSession(sessionId: string): Promise<TONConnectSession | undefined> {
return await this.swiftSessionsManager.getSession(sessionId);
}

async getSessionByDomain(domain: string): Promise<TONConnectSession | undefined> {
return await this.swiftSessionsManager.getSessionByDomain(domain);
}

async getSessions(): Promise<TONConnectSession[]> {
return await this.swiftSessionsManager.getSessions();
}

async getSessionsForWallet(walletId: WalletId): Promise<TONConnectSession[]> {
return await this.swiftSessionsManager.getSessionsForWallet(walletId);
}

async removeSession(sessionId: string): Promise<void> {
await this.swiftSessionsManager.removeSession(sessionId);
}

async removeSessionsForWallet(walletId: WalletId): Promise<void> {
await this.swiftSessionsManager.removeSessionsForWallet(walletId);
}

async clearSessions(): Promise<void> {
await this.swiftSessionsManager.clearSessions();
}
}
12 changes: 10 additions & 2 deletions packages/walletkit-ios-bridge/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@ import type { WalletAdapter } from '@ton/walletkit';
import { SwiftStorageAdapter } from './SwiftStorageAdapter';
import { SwiftWalletAdapter } from './SwiftWalletAdapter';
import { SwiftAPIClientAdapter } from './SwiftAPIClientAdapter';
import { SwiftTONConnectSessionsManager } from './SwiftTONConnectSessionsManager';

declare global {
interface Window {
walletKit?: any;
initWalletKit: (configuration, storage, bridgeTransport: (response) => void, apiClients) => Promise<void>;
initWalletKit: (
configuration,
storage,
bridgeTransport: (response) => void,
sessionManager,
apiClients,
) => Promise<void>;
}
}

window.initWalletKit = async (configuration, storage, bridgeTransport, apiClients) => {
window.initWalletKit = async (configuration, storage, bridgeTransport, sessionManager, apiClients) => {
console.log('🚀 WalletKit iOS Bridge starting...');

console.log('Creating WalletKit instance with configuration', configuration);
Expand Down Expand Up @@ -59,6 +66,7 @@ window.initWalletKit = async (configuration, storage, bridgeTransport, apiClient
networks,
walletManifest: configuration.walletManifest,
deviceInfo: configuration.deviceInfo,
sessionManager: sessionManager ? new SwiftTONConnectSessionsManager(sessionManager) : undefined,
bridge: configuration.bridge,
eventProcessor: configuration.eventsConfiguration,
storage: storage ? new SwiftStorageAdapter(storage) : new MemoryStorageAdapter({}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

// TONConnect Session Manager abstraction for Dependency Inversion

import type { DAppInfo, TONConnectSession } from '../models';
import type { WalletId } from '../../utils/walletId';
import type { Wallet } from '.';

/**
* Abstraction for session management in TONConnect protocol.
* Provides interface for session CRUD operations and lifecycle management.
*/
export interface TONConnectSessionManager {
/**
* Initialize the session manager
* Needed for backward compatibility with existing codebase
* Used to ensure that sessions are reloaded fro mstorage to local cache in browser extension
* */
initialize(): Promise<void>;

/**
* Create a new session
* @param sessionId - Unique session identifier
* @param dAppInfo - Information about the dApp (name, url, iconUrl, description)
* @param wallet - The wallet to associate with this session
* @param options - Additional options for session creation
*/
createSession(
sessionId: string,
dAppInfo: DAppInfo,
wallet: Wallet,
isJsBridge: boolean,
): Promise<TONConnectSession>;

/**
* Get session by ID
* @param sessionId - The session ID to retrieve
*/
getSession(sessionId: string): Promise<TONConnectSession | undefined>;

/**
* Get session by domain
* @param domain - The domain to search for
*/
getSessionByDomain(domain: string): Promise<TONConnectSession | undefined>;

/**
* Get all sessions as array
*/
getSessions(): Promise<TONConnectSession[]>;

/**
* Get sessions for specific wallet by wallet ID
* @param walletId - The wallet ID to filter by
*/
getSessionsForWallet(walletId: WalletId): Promise<TONConnectSession[]>;

/**
* Remove session by ID
* @param sessionId - The session ID to remove
*/
removeSession(sessionId: string): Promise<void>;

/**
* Remove all sessions for a wallet by wallet ID
* @param walletId - Wallet ID string
*/
removeSessionsForWallet(walletId: WalletId): Promise<void>;

/**
* Clear all sessions
*/
clearSessions(): Promise<void>;
}
1 change: 1 addition & 0 deletions packages/walletkit/src/api/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
export type { Wallet, WalletTonInterface, WalletNftInterface, WalletJettonInterface } from './Wallet';
export type { WalletAdapter } from './WalletAdapter';
export type { WalletSigner, ISigner } from './WalletSigner';
export type { TONConnectSessionManager } from './TONConnectSessionManager';
48 changes: 48 additions & 0 deletions packages/walletkit/src/api/models/bridge/TONConnectSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) TonTech.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type { UserFriendlyAddress, WalletId } from '../../..';

export interface TONConnectSession {
sessionId: string;

walletId: WalletId;
walletAddress: UserFriendlyAddress;
createdAt: string; // date
lastActivityAt: string; // date
privateKey: string;
publicKey: string;
domain: string;

/**
* Display name of the dApp
*/
dAppName?: string;

/**
* Brief description of the dApp's purpose
*/
dAppDescription?: string;

/**
* Main website URL of the dApp
* @format url
*/
dAppUrl?: string;

/**
* Icon/logo URL of the dApp
* @format url
*/
dAppIconUrl?: string;

// Bridge type indicator (needed to determine how to send disconnect events)
isJsBridge?: boolean; // true if session was created via JS Bridge, false/undefined for HTTP Bridge

schemaVersion: number;
}
1 change: 1 addition & 0 deletions packages/walletkit/src/api/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type { SignDataRequestEvent, SignDataRequestEventPreview, SignDataPreview
export type { TransactionApprovalResponse } from './bridge/TransactionApprovalResponse';
export type { TransactionRequestEvent, TransactionRequestEventPreview } from './bridge/TransactionRequestEvent';
export type { RequestErrorEvent } from './bridge/RequestErrorEvent';
export type { TONConnectSession } from './bridge/TONConnectSession';

// Jetton models
export type { Jetton } from './jettons/Jetton';
Expand Down
46 changes: 26 additions & 20 deletions packages/walletkit/src/core/BridgeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { SessionCrypto } from '@tonconnect/protocol';
import type { ClientConnection, WalletConsumer } from '@tonconnect/bridge-sdk';
import { BridgeProvider } from '@tonconnect/bridge-sdk';

import type { BridgeConfig, RawBridgeEvent, SessionData } from '../types/internal';
import type { BridgeConfig, RawBridgeEvent } from '../types/internal';
import type { Storage } from '../storage';
import type { EventStore } from '../types/durableEvents';
import type { EventEmitter } from './EventEmitter';
import { globalLogger } from './Logger';
import type { SessionManager } from './SessionManager';
import type { TONConnectSessionManager } from '../api/interfaces/TONConnectSessionManager';
import type { EventRouter } from './EventRouter';
import type {
BridgeEventMessageInfo,
Expand All @@ -30,14 +30,14 @@ import { WalletKitError, ERROR_CODES } from '../errors';
import type { Analytics, AnalyticsManager } from '../analytics';
import type { TonWalletKitOptions } from '../types/config';
import { TONCONNECT_BRIDGE_RESPONSE } from '../bridge/JSBridgeInjector';
import type { BridgeEvent } from '../api/models';
import type { BridgeEvent, TONConnectSession } from '../api/models';

const log = globalLogger.createChild('BridgeManager');

export class BridgeManager {
private config: BridgeConfig;
private bridgeProvider?: BridgeProvider<WalletConsumer>;
private sessionManager: SessionManager;
private sessionManager: TONConnectSessionManager;
private storage: Storage;
private isConnected = false;
private reconnectAttempts = 0;
Expand All @@ -62,7 +62,7 @@ export class BridgeManager {
constructor(
walletManifest: WalletInfo | undefined,
config: BridgeConfig | undefined,
sessionManager: SessionManager,
sessionManager: TONConnectSessionManager,
storage: Storage,
eventStore: EventStore,
eventRouter: EventRouter,
Expand Down Expand Up @@ -188,7 +188,7 @@ export class BridgeManager {
event: BridgeEvent,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
response: any,
_session?: SessionData,
providedSessionCrypto?: SessionCrypto,
): Promise<void> {
if (event.isLocal) {
return;
Expand Down Expand Up @@ -220,19 +220,25 @@ export class BridgeManager {
);
}

const session = _session ?? (await this.sessionManager.getSession(sessionId));
if (!session) {
throw new WalletKitError(ERROR_CODES.SESSION_NOT_FOUND, `Session not found for response`, undefined, {
sessionId,
eventId: event.id,
});
let sessionCrypto = providedSessionCrypto;

if (!sessionCrypto) {
const session = await this.sessionManager.getSession(sessionId);

if (session) {
sessionCrypto = new SessionCrypto({
publicKey: session.publicKey,
secretKey: session.privateKey,
});
} else {
throw new WalletKitError(ERROR_CODES.SESSION_NOT_FOUND, `Session not found for response`, undefined, {
sessionId,
eventId: event.id,
});
}
}

try {
const sessionCrypto = new SessionCrypto({
publicKey: session.publicKey,
secretKey: session.privateKey,
});
await this.bridgeProvider.send(response, sessionCrypto, sessionId, {
traceId: event?.traceId,
});
Expand Down Expand Up @@ -261,7 +267,7 @@ export class BridgeManager {
response: any,
options?: {
traceId?: string;
session?: SessionData;
session?: TONConnectSession;
},
): Promise<void> {
const source = this.config.jsBridgeKey + '-tonconnect';
Expand Down Expand Up @@ -346,7 +352,7 @@ export class BridgeManager {
// }

private async getClients(): Promise<ClientConnection[]> {
return this.sessionManager.getSessions().map((session) => ({
return (await this.sessionManager.getSessions()).map((session) => ({
session: new SessionCrypto({
publicKey: session.publicKey,
secretKey: session.privateKey.length > 64 ? session.privateKey.slice(0, 64) : session.privateKey,
Expand Down Expand Up @@ -606,7 +612,7 @@ export class BridgeManager {
rawEvent.dAppInfo = {
name: session.dAppName,
description: session.dAppDescription,
url: session.dAppIconUrl,
url: session.dAppUrl,
iconUrl: session.dAppIconUrl,
};
}
Expand All @@ -627,7 +633,7 @@ export class BridgeManager {
rawEvent.dAppInfo = {
name: session.dAppName,
description: session.dAppDescription,
url: session.dAppIconUrl,
url: session.dAppUrl,
iconUrl: session.dAppIconUrl,
};

Expand Down
Loading
Loading