diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 4a36f331..f4091eae 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -56,6 +56,7 @@ "webpack-cli": "^5.0.0", "rollup": "^3.18.0", "@rollup/plugin-typescript": "^11.0.0", + "@rollup/plugin-replace": "5.0.5", "vite": "^4.2.1", "vite-tsconfig-paths": "^4.0.5", "vitest": "^0.29.2", diff --git a/packages/sdk/rollup.config.mjs b/packages/sdk/rollup.config.mjs index ec163a36..8fc39464 100644 --- a/packages/sdk/rollup.config.mjs +++ b/packages/sdk/rollup.config.mjs @@ -1,24 +1,38 @@ -import typescript from '@rollup/plugin-typescript'; +import typescript from "@rollup/plugin-typescript"; +import packageJson from "./package.json" with { type: "json" }; +import replace from "@rollup/plugin-replace"; + +const version = packageJson.version; export default [ - { - input: 'src/index.ts', - output: { - format: 'esm', - file: './lib/esm/index.mjs', - sourcemap: true - }, - plugins: [typescript()], - external: ['@tonconnect/protocol', '@tonconnect/isomorphic-fetch', '@tonconnect/isomorphic-eventsource'] + { + input: "src/index.ts", + output: { + format: "esm", + file: "./lib/esm/index.mjs", + sourcemap: true + }, + plugins: [ + replace({ + TON_CONNECT_SDK_VERSION: JSON.stringify(version) + }), + typescript() + ], + external: ["@tonconnect/protocol", "@tonconnect/isomorphic-fetch", "@tonconnect/isomorphic-eventsource"] + }, + { + input: "src/index.ts", + output: { + format: "cjs", + file: "./lib/cjs/index.cjs", + sourcemap: true }, - { - input: 'src/index.ts', - output: { - format: 'cjs', - file: './lib/cjs/index.cjs', - sourcemap: true - }, - plugins: [typescript()], - external: ['@tonconnect/protocol', '@tonconnect/isomorphic-fetch', '@tonconnect/isomorphic-eventsource'] - } + plugins: [ + replace({ + TON_CONNECT_SDK_VERSION: JSON.stringify(version) + }), + typescript() + ], + external: ["@tonconnect/protocol", "@tonconnect/isomorphic-fetch", "@tonconnect/isomorphic-eventsource"] + } ]; diff --git a/packages/sdk/src/constants/version.ts b/packages/sdk/src/constants/version.ts new file mode 100644 index 00000000..ee9412c5 --- /dev/null +++ b/packages/sdk/src/constants/version.ts @@ -0,0 +1,3 @@ +declare const TON_CONNECT_SDK_VERSION: string; + +export const tonConnectSdkVersion = TON_CONNECT_SDK_VERSION; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 0d3a7a4c..bd04a5e7 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -5,7 +5,11 @@ export { IStorage } from './storage/models/storage.interface'; export { TonConnect as default } from './ton-connect'; export { WalletsListManager } from './wallets-list-manager'; export { ITonConnect } from './ton-connect.interface'; -export type { EventDispatcher } from './tracker/event-dispatcher'; +export type { + EventDispatcher, + RemoveTonConnectPrefix, + AddTonConnectPrefix +} from './tracker/event-dispatcher'; export { createConnectionStartedEvent, createConnectionErrorEvent, @@ -16,7 +20,10 @@ export { createDisconnectionEvent, createTransactionSentForSignatureEvent, createTransactionSigningFailedEvent, - createTransactionSignedEvent + createTransactionSignedEvent, + createRequestVersionEvent, + createResponseVersionEvent, + createVersionInfo } from './tracker/types'; export type { AuthType, @@ -36,7 +43,12 @@ export type { TransactionSignedEvent, TransactionSentForSignatureEvent, TransactionSigningFailedEvent, - SdkActionEvent + SdkActionEvent, + RequestVersionEvent, + ResponseVersionEvent, + VersionEvent, + Version, + WithoutVersion } from './tracker/types'; export { BrowserEventDispatcher } from './tracker/browser-event-dispatcher'; export type { TonAddressItem, TonProofItem, ConnectItem } from '@tonconnect/protocol'; diff --git a/packages/sdk/src/ton-connect.ts b/packages/sdk/src/ton-connect.ts index 698a9b2e..700bc703 100644 --- a/packages/sdk/src/ton-connect.ts +++ b/packages/sdk/src/ton-connect.ts @@ -41,9 +41,10 @@ import { WalletsListManager } from 'src/wallets-list-manager'; import { WithoutIdDistributive } from 'src/utils/types'; import { checkSendTransactionSupport } from 'src/utils/feature-support'; import { callForSuccess } from 'src/utils/call-for-success'; -import { logError } from 'src/utils/log'; +import { logDebug, logError } from 'src/utils/log'; import { createAbortController } from 'src/utils/create-abort-controller'; import { TonConnectTracker } from 'src/tracker/ton-connect-tracker'; +import { tonConnectSdkVersion } from 'src/constants/version'; export class TonConnect implements ITonConnect { private static readonly walletsList = new WalletsListManager(); @@ -128,7 +129,10 @@ export class TonConnect implements ITonConnect { cacheTTLMs: options?.walletsListCacheTTLMs }); - this.tracker = new TonConnectTracker(options?.eventDispatcher); + this.tracker = new TonConnectTracker({ + eventDispatcher: options?.eventDispatcher, + tonConnectSdkVersion: tonConnectSdkVersion + }); if (!this.dappSettings.manifestUrl) { throw new DappMetadataError( @@ -333,7 +337,7 @@ export class TonConnect implements ITonConnect { this.provider = provider; provider.listen(this.walletEventsListener.bind(this)); - const onAbortRestore = () => { + const onAbortRestore = (): void => { this.tracker.trackConnectionRestoringError('Connection restoring was aborted'); provider?.closeConnection(); provider = null; @@ -345,7 +349,7 @@ export class TonConnect implements ITonConnect { await provider?.restoreConnection({ openingDeadlineMS: options?.openingDeadlineMS, signal: _options.signal - }) + }); abortController.signal.removeEventListener('abort', onAbortRestore); if (this.connected) { @@ -429,7 +433,12 @@ export class TonConnect implements ITonConnect { ); if (sendTransactionParser.isError(response)) { - this.tracker.trackTransactionSigningFailed(this.wallet, transaction, response.error.message, response.error.code); + this.tracker.trackTransactionSigningFailed( + this.wallet, + transaction, + response.error.message, + response.error.code + ); return sendTransactionParser.parseAndThrowError(response); } @@ -496,7 +505,7 @@ export class TonConnect implements ITonConnect { if (document.hidden) { this.pauseConnection(); } else { - this.unPauseConnection().catch(e => logError('Cannot unpause connection', e)); + this.unPauseConnection().catch(); } }); } catch (e) { @@ -571,11 +580,11 @@ export class TonConnect implements ITonConnect { const error = connectErrorsParser.parseError(connectEventError); this.statusChangeErrorSubscriptions.forEach(errorsHandler => errorsHandler(error)); - console.debug(error); - this.tracker.trackConnectionError(connectEventError.message, connectEventError.code) + logDebug(error); + this.tracker.trackConnectionError(connectEventError.message, connectEventError.code); if (error instanceof ManifestNotFoundError || error instanceof ManifestContentErrorError) { - console.error(error); + logError(error); throw error; } } diff --git a/packages/sdk/src/tracker/browser-event-dispatcher.ts b/packages/sdk/src/tracker/browser-event-dispatcher.ts index 389d9020..dbd8d3b3 100644 --- a/packages/sdk/src/tracker/browser-event-dispatcher.ts +++ b/packages/sdk/src/tracker/browser-event-dispatcher.ts @@ -1,10 +1,10 @@ import { getWindow } from 'src/utils/web-api'; -import { EventDispatcher } from 'src/tracker/event-dispatcher'; +import { AddTonConnectPrefix, EventDispatcher, RemoveTonConnectPrefix } from './event-dispatcher'; /** * A concrete implementation of EventDispatcher that dispatches events to the browser window. */ -export class BrowserEventDispatcher<T> implements EventDispatcher<T> { +export class BrowserEventDispatcher<T extends { type: string }> implements EventDispatcher<T> { /** * The window object, possibly undefined in a server environment. * @private @@ -17,8 +17,35 @@ export class BrowserEventDispatcher<T> implements EventDispatcher<T> { * @param eventDetails - The details of the event to dispatch. * @returns A promise that resolves when the event has been dispatched. */ - public async dispatchEvent(eventName: string, eventDetails: T): Promise<void> { + public async dispatchEvent<P extends AddTonConnectPrefix<T['type']>>( + eventName: P, + eventDetails: T & { type: RemoveTonConnectPrefix<P> } + ): Promise<void> { const event = new CustomEvent<T>(eventName, { detail: eventDetails }); this.window?.dispatchEvent(event); } + + /** + * Adds an event listener to the browser window. + * @param eventName - The name of the event to listen for. + * @param listener - The listener to add. + * @param options - The options for the listener. + * @returns A function that removes the listener. + */ + public async addEventListener<P extends AddTonConnectPrefix<T['type']>>( + eventName: P, + listener: (event: CustomEvent<T & { type: RemoveTonConnectPrefix<P> }>) => void, + options?: AddEventListenerOptions + ): Promise<() => void> { + this.window?.addEventListener( + eventName, + listener as EventListener | EventListenerObject, + options + ); + return () => + this.window?.removeEventListener( + eventName, + listener as EventListener | EventListenerObject + ); + } } diff --git a/packages/sdk/src/tracker/event-dispatcher.ts b/packages/sdk/src/tracker/event-dispatcher.ts index 4a6d580c..20cfa8ae 100644 --- a/packages/sdk/src/tracker/event-dispatcher.ts +++ b/packages/sdk/src/tracker/event-dispatcher.ts @@ -1,11 +1,38 @@ +/** + * Removes the `ton-connect-` and `ton-connect-ui-` prefixes from the given string. + */ +export type RemoveTonConnectPrefix<T> = T extends `ton-connect-ui-${infer Rest}` + ? Rest + : T extends `ton-connect-${infer Rest}` + ? Rest + : T; + +export type AddTonConnectPrefix<T extends string> = `ton-connect-${T}` | `ton-connect-ui-${T}`; + /** * Interface for an event dispatcher that sends events. */ -export interface EventDispatcher<T> { +export interface EventDispatcher<T extends { type: string }> { /** * Dispatches an event with the given name and details. * @param eventName - The name of the event to dispatch. * @param eventDetails - The details of the event to dispatch. */ - dispatchEvent(eventName: string, eventDetails: T): Promise<void>; + dispatchEvent<P extends AddTonConnectPrefix<T['type']>>( + eventName: P, + eventDetails: T & { type: RemoveTonConnectPrefix<P> } + ): Promise<void>; + + /** + * Adds an event listener. + * @param eventName - The name of the event to listen for. + * @param listener - The listener to add. + * @param options - The options for the listener. + * @returns A function that removes the listener. + */ + addEventListener<P extends AddTonConnectPrefix<T['type']>>( + eventName: P, + listener: (event: CustomEvent<T & { type: RemoveTonConnectPrefix<P> }>) => void, + options?: AddEventListenerOptions + ): Promise<() => void>; } diff --git a/packages/sdk/src/tracker/ton-connect-tracker.ts b/packages/sdk/src/tracker/ton-connect-tracker.ts index bcdc2c6a..c0af6ee1 100644 --- a/packages/sdk/src/tracker/ton-connect-tracker.ts +++ b/packages/sdk/src/tracker/ton-connect-tracker.ts @@ -1,16 +1,40 @@ import { + createConnectionCompletedEvent, + createConnectionErrorEvent, createConnectionRestoringCompletedEvent, createConnectionRestoringErrorEvent, createConnectionRestoringStartedEvent, + createConnectionStartedEvent, createDisconnectionEvent, + createRequestVersionEvent, + createResponseVersionEvent, createTransactionSentForSignatureEvent, createTransactionSignedEvent, createTransactionSigningFailedEvent, - SdkActionEvent, createConnectionStartedEvent, createConnectionErrorEvent, createConnectionCompletedEvent + createVersionInfo, + ResponseVersionEvent, + SdkActionEvent, + Version, + WithoutVersion } from './types'; import { EventDispatcher } from 'src/tracker/event-dispatcher'; import { BrowserEventDispatcher } from 'src/tracker/browser-event-dispatcher'; +/** + * Options for the TonConnect tracker. + */ +export type TonConnectTrackerOptions = { + /** + * Event dispatcher to track user actions. + * @default new BrowserEventDispatcher() + */ + eventDispatcher?: EventDispatcher<SdkActionEvent> | null; + /** + * TonConnect SDK version. + */ + tonConnectSdkVersion: string; +}; + /** * Tracker for TonConnect user actions, such as transaction signing, connection, etc. * @@ -42,14 +66,85 @@ export class TonConnectTracker { */ private readonly eventPrefix = 'ton-connect-'; + /** + * TonConnect SDK version. + */ + private readonly tonConnectSdkVersion: string; + + /** + * TonConnect UI version. + */ + private tonConnectUiVersion: string | null = null; + + /** + * Version of the library. + */ + get version(): Version { + return createVersionInfo({ + ton_connect_sdk_lib: this.tonConnectSdkVersion, + ton_connect_ui_lib: this.tonConnectUiVersion + }); + } + /** * Event dispatcher to track user actions. By default, it uses `window.dispatchEvent` for browser environment. * @private */ private readonly eventDispatcher: EventDispatcher<SdkActionEvent>; - constructor(eventDispatcher?: EventDispatcher<SdkActionEvent> | null) { - this.eventDispatcher = eventDispatcher ?? new BrowserEventDispatcher(); + constructor(options: TonConnectTrackerOptions) { + this.eventDispatcher = options?.eventDispatcher ?? new BrowserEventDispatcher(); + this.tonConnectSdkVersion = options.tonConnectSdkVersion; + + this.init().catch(); + } + + /** + * Called once when the tracker is created and request version other libraries. + */ + private async init(): Promise<void> { + try { + await this.setRequestVersionHandler(); + this.tonConnectUiVersion = await this.requestTonConnectUiVersion(); + } catch (e) {} + } + + /** + * Set request version handler. + * @private + */ + private async setRequestVersionHandler(): Promise<void> { + await this.eventDispatcher.addEventListener('ton-connect-request-version', async () => { + await this.eventDispatcher.dispatchEvent( + 'ton-connect-response-version', + createResponseVersionEvent(this.tonConnectSdkVersion) + ); + }); + } + + /** + * Request TonConnect UI version. + * @private + */ + private async requestTonConnectUiVersion(): Promise<string> { + return new Promise(async (resolve, reject) => { + try { + await this.eventDispatcher.addEventListener( + 'ton-connect-ui-response-version', + (event: CustomEvent<ResponseVersionEvent>) => { + resolve(event.detail.version); + }, + { once: true } + ); + + await this.eventDispatcher.dispatchEvent( + 'ton-connect-ui-request-version', + createRequestVersionEvent() + ); + } catch (e) { + reject(e); + } + }); } /** @@ -59,8 +154,9 @@ export class TonConnectTracker { */ private dispatchUserActionEvent(eventDetails: SdkActionEvent): void { try { - const eventName = `${this.eventPrefix}${eventDetails.type}`; - this.eventDispatcher.dispatchEvent(eventName, eventDetails).catch(); + this.eventDispatcher + .dispatchEvent(`${this.eventPrefix}${eventDetails.type}`, eventDetails) + .catch(); } catch (e) {} } @@ -68,9 +164,11 @@ export class TonConnectTracker { * Track connection init event. * @param args */ - public trackConnectionStarted(...args: Parameters<typeof createConnectionStartedEvent>): void { + public trackConnectionStarted( + ...args: WithoutVersion<Parameters<typeof createConnectionStartedEvent>> + ): void { try { - const event = createConnectionStartedEvent(...args); + const event = createConnectionStartedEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -80,10 +178,10 @@ export class TonConnectTracker { * @param args */ public trackConnectionCompleted( - ...args: Parameters<typeof createConnectionCompletedEvent> + ...args: WithoutVersion<Parameters<typeof createConnectionCompletedEvent>> ): void { try { - const event = createConnectionCompletedEvent(...args); + const event = createConnectionCompletedEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -92,9 +190,11 @@ export class TonConnectTracker { * Track connection error event. * @param args */ - public trackConnectionError(...args: Parameters<typeof createConnectionErrorEvent>): void { + public trackConnectionError( + ...args: WithoutVersion<Parameters<typeof createConnectionErrorEvent>> + ): void { try { - const event = createConnectionErrorEvent(...args); + const event = createConnectionErrorEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -104,10 +204,10 @@ export class TonConnectTracker { * @param args */ public trackConnectionRestoringStarted( - ...args: Parameters<typeof createConnectionRestoringStartedEvent> + ...args: WithoutVersion<Parameters<typeof createConnectionRestoringStartedEvent>> ): void { try { - const event = createConnectionRestoringStartedEvent(...args); + const event = createConnectionRestoringStartedEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -117,10 +217,10 @@ export class TonConnectTracker { * @param args */ public trackConnectionRestoringCompleted( - ...args: Parameters<typeof createConnectionRestoringCompletedEvent> + ...args: WithoutVersion<Parameters<typeof createConnectionRestoringCompletedEvent>> ): void { try { - const event = createConnectionRestoringCompletedEvent(...args); + const event = createConnectionRestoringCompletedEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -130,10 +230,10 @@ export class TonConnectTracker { * @param args */ public trackConnectionRestoringError( - ...args: Parameters<typeof createConnectionRestoringErrorEvent> + ...args: WithoutVersion<Parameters<typeof createConnectionRestoringErrorEvent>> ): void { try { - const event = createConnectionRestoringErrorEvent(...args); + const event = createConnectionRestoringErrorEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -142,9 +242,11 @@ export class TonConnectTracker { * Track disconnect event. * @param args */ - public trackDisconnection(...args: Parameters<typeof createDisconnectionEvent>): void { + public trackDisconnection( + ...args: WithoutVersion<Parameters<typeof createDisconnectionEvent>> + ): void { try { - const event = createDisconnectionEvent(...args); + const event = createDisconnectionEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -154,10 +256,10 @@ export class TonConnectTracker { * @param args */ public trackTransactionSentForSignature( - ...args: Parameters<typeof createTransactionSentForSignatureEvent> + ...args: WithoutVersion<Parameters<typeof createTransactionSentForSignatureEvent>> ): void { try { - const event = createTransactionSentForSignatureEvent(...args); + const event = createTransactionSentForSignatureEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -166,9 +268,11 @@ export class TonConnectTracker { * Track transaction signed event. * @param args */ - public trackTransactionSigned(...args: Parameters<typeof createTransactionSignedEvent>): void { + public trackTransactionSigned( + ...args: WithoutVersion<Parameters<typeof createTransactionSignedEvent>> + ): void { try { - const event = createTransactionSignedEvent(...args); + const event = createTransactionSignedEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } @@ -178,10 +282,10 @@ export class TonConnectTracker { * @param args */ public trackTransactionSigningFailed( - ...args: Parameters<typeof createTransactionSigningFailedEvent> + ...args: WithoutVersion<Parameters<typeof createTransactionSigningFailedEvent>> ): void { try { - const event = createTransactionSigningFailedEvent(...args); + const event = createTransactionSigningFailedEvent(this.version, ...args); this.dispatchUserActionEvent(event); } catch (e) {} } diff --git a/packages/sdk/src/tracker/types.ts b/packages/sdk/src/tracker/types.ts index 1e2623e2..a5399be9 100644 --- a/packages/sdk/src/tracker/types.ts +++ b/packages/sdk/src/tracker/types.ts @@ -1,6 +1,80 @@ import { CONNECT_EVENT_ERROR_CODES, ConnectItem, SEND_TRANSACTION_ERROR_CODES } from '@tonconnect/protocol'; import { SendTransactionRequest, SendTransactionResponse, Wallet } from 'src/models'; +/** + * Request TON Connect UI version. + */ +export type RequestVersionEvent = { + /** + * Event type. + */ + type: 'request-version'; +}; + +/** + * Create a request version event. + */ +export function createRequestVersionEvent(): RequestVersionEvent { + return { + type: 'request-version' + }; +} + +/** + * Response TON Connect UI version. + */ +export type ResponseVersionEvent = { + /** + * Event type. + */ + type: 'response-version'; + /** + * TON Connect UI version. + */ + version: string; +}; + +/** + * Create a response version event. + * @param version + */ +export function createResponseVersionEvent(version: string): ResponseVersionEvent { + return { + type: 'response-version', + version: version + }; +} + +/** + * Version events. + */ +export type VersionEvent = RequestVersionEvent | ResponseVersionEvent; + +/** + * Version of the TON Connect SDK and TON Connect UI. + */ +export type Version = { + /** + * TON Connect SDK version. + */ + ton_connect_sdk_lib: string | null; + /** + * TON Connect UI version. + */ + ton_connect_ui_lib: string | null; +}; + +/** + * Create a version info. + * @param version + */ +export function createVersionInfo(version: Version): Version { + return { + ton_connect_sdk_lib: version.ton_connect_sdk_lib, + ton_connect_ui_lib: version.ton_connect_ui_lib + }; +} + /** * Requested authentication type: 'ton_addr' or 'ton_proof'. */ @@ -38,10 +112,10 @@ export type ConnectionInfo = { * Wallet provider. */ provider: 'http' | 'injected' | null; - }; + } & Version; }; -function createConnectionInfo(wallet: Wallet | null): ConnectionInfo { +function createConnectionInfo(version: Version, wallet: Wallet | null): ConnectionInfo { const isTonProof = wallet?.connectItems?.tonProof && 'proof' in wallet.connectItems.tonProof; const authType: AuthType = isTonProof ? 'ton_proof' : 'ton_addr'; @@ -52,7 +126,8 @@ function createConnectionInfo(wallet: Wallet | null): ConnectionInfo { auth_type: authType, custom_data: { chain_id: wallet?.account?.chain ?? null, - provider: wallet?.provider ?? null + provider: wallet?.provider ?? null, + ...createVersionInfo(version) } }; } @@ -65,14 +140,19 @@ export type ConnectionStartedEvent = { * Event type. */ type: 'connection-started'; + /** + * Custom data for the connection. + */ + custom_data: Version; }; /** * Create a connection init event. */ -export function createConnectionStartedEvent(): ConnectionStartedEvent { +export function createConnectionStartedEvent(version: Version): ConnectionStartedEvent { return { - type: 'connection-started' + type: 'connection-started', + custom_data: createVersionInfo(version) }; } @@ -84,16 +164,25 @@ export type ConnectionCompletedEvent = { * Event type. */ type: 'connection-completed'; + /** + * Connection success flag. + */ + is_success: true; } & ConnectionInfo; /** * Create a connection completed event. + * @param version * @param wallet */ -export function createConnectionCompletedEvent(wallet: Wallet | null): ConnectionCompletedEvent { +export function createConnectionCompletedEvent( + version: Version, + wallet: Wallet | null +): ConnectionCompletedEvent { return { type: 'connection-completed', - ...createConnectionInfo(wallet) + is_success: true, + ...createConnectionInfo(version, wallet) }; } @@ -105,6 +194,10 @@ export type ConnectionErrorEvent = { * Event type. */ type: 'connection-error'; + /** + * Connection success flag. + */ + is_success: false; /** * Reason for the error. */ @@ -113,21 +206,29 @@ export type ConnectionErrorEvent = { * Error code. */ error_code: CONNECT_EVENT_ERROR_CODES | null; + /** + * Custom data for the connection. + */ + custom_data: Version; }; /** * Create a connection error event. + * @param version * @param error_message * @param errorCode */ export function createConnectionErrorEvent( + version: Version, error_message: string, errorCode: CONNECT_EVENT_ERROR_CODES | void ): ConnectionErrorEvent { return { type: 'connection-error', + is_success: false, error_message: error_message, - error_code: errorCode ?? null + error_code: errorCode ?? null, + custom_data: createVersionInfo(version) }; } @@ -147,14 +248,21 @@ export type ConnectionRestoringStartedEvent = { * Event type. */ type: 'connection-restoring-started'; + /** + * Custom data for the connection. + */ + custom_data: Version; }; /** * Create a connection restoring started event. */ -export function createConnectionRestoringStartedEvent(): ConnectionRestoringStartedEvent { +export function createConnectionRestoringStartedEvent( + version: Version +): ConnectionRestoringStartedEvent { return { - type: 'connection-restoring-started' + type: 'connection-restoring-started', + custom_data: createVersionInfo(version) }; } @@ -166,18 +274,25 @@ export type ConnectionRestoringCompletedEvent = { * Event type. */ type: 'connection-restoring-completed'; + /** + * Connection success flag. + */ + is_success: true; } & ConnectionInfo; /** * Create a connection restoring completed event. + * @param version * @param wallet */ export function createConnectionRestoringCompletedEvent( + version: Version, wallet: Wallet | null ): ConnectionRestoringCompletedEvent { return { type: 'connection-restoring-completed', - ...createConnectionInfo(wallet) + is_success: true, + ...createConnectionInfo(version, wallet) }; } @@ -189,22 +304,34 @@ export type ConnectionRestoringErrorEvent = { * Event type. */ type: 'connection-restoring-error'; + /** + * Connection success flag. + */ + is_success: false; /** * Reason for the error. */ error_message: string; + /** + * Custom data for the connection. + */ + custom_data: Version; }; /** * Create a connection restoring error event. + * @param version * @param errorMessage */ export function createConnectionRestoringErrorEvent( + version: Version, errorMessage: string ): ConnectionRestoringErrorEvent { return { type: 'connection-restoring-error', - error_message: errorMessage + is_success: false, + error_message: errorMessage, + custom_data: createVersionInfo(version) }; } @@ -275,16 +402,18 @@ export type TransactionSentForSignatureEvent = { /** * Create a transaction init event. + * @param version * @param wallet * @param transaction */ export function createTransactionSentForSignatureEvent( + version: Version, wallet: Wallet | null, transaction: SendTransactionRequest ): TransactionSentForSignatureEvent { return { type: 'transaction-sent-for-signature', - ...createConnectionInfo(wallet), + ...createConnectionInfo(version, wallet), ...createTransactionInfo(wallet, transaction) }; } @@ -297,6 +426,10 @@ export type TransactionSignedEvent = { * Event type. */ type: 'transaction-signed'; + /** + * Connection success flag. + */ + is_success: true; /** * Signed transaction. */ @@ -306,19 +439,22 @@ export type TransactionSignedEvent = { /** * Create a transaction signed event. + * @param version * @param wallet * @param transaction * @param signedTransaction */ export function createTransactionSignedEvent( + version: Version, wallet: Wallet | null, transaction: SendTransactionRequest, signedTransaction: SendTransactionResponse ): TransactionSignedEvent { return { type: 'transaction-signed', + is_success: true, signed_transaction: signedTransaction.boc, - ...createConnectionInfo(wallet), + ...createConnectionInfo(version, wallet), ...createTransactionInfo(wallet, transaction) }; } @@ -331,6 +467,10 @@ export type TransactionSigningFailedEvent = { * Event type. */ type: 'transaction-signing-failed'; + /** + * Connection success flag. + */ + is_success: false; /** * Reason for the error. */ @@ -344,12 +484,14 @@ export type TransactionSigningFailedEvent = { /** * Create a transaction error event. + * @param version * @param wallet * @param transaction * @param errorMessage * @param errorCode */ export function createTransactionSigningFailedEvent( + version: Version, wallet: Wallet | null, transaction: SendTransactionRequest, errorMessage: string, @@ -357,9 +499,10 @@ export function createTransactionSigningFailedEvent( ): TransactionSigningFailedEvent { return { type: 'transaction-signing-failed', + is_success: false, error_message: errorMessage, error_code: errorCode ?? null, - ...createConnectionInfo(wallet), + ...createConnectionInfo(version, wallet), ...createTransactionInfo(wallet, transaction) }; } @@ -388,18 +531,20 @@ export type DisconnectionEvent = { /** * Create a disconnect event. + * @param version * @param wallet * @param scope * @returns */ export function createDisconnectionEvent( + version: Version, wallet: Wallet | null, scope: 'dapp' | 'wallet' ): DisconnectionEvent { return { type: 'disconnection', scope: scope, - ...createConnectionInfo(wallet) + ...createConnectionInfo(version, wallet) }; } @@ -407,7 +552,13 @@ export function createDisconnectionEvent( * User action events. */ export type SdkActionEvent = + | VersionEvent | ConnectionEvent | ConnectionRestoringEvent | DisconnectionEvent | TransactionSigningEvent; + +/** + * Parameters without version field. + */ +export type WithoutVersion<T> = T extends [Version, ...infer Rest] ? [...Rest] : never; diff --git a/packages/sdk/src/utils/resource.ts b/packages/sdk/src/utils/resource.ts index 24b40cd8..8953c716 100644 --- a/packages/sdk/src/utils/resource.ts +++ b/packages/sdk/src/utils/resource.ts @@ -1,5 +1,4 @@ import { TonConnectError } from 'src/errors'; -import { logError } from 'src/utils/log'; import { delay } from 'src/utils/delay'; import { createAbortController } from 'src/utils/create-abort-controller'; @@ -95,9 +94,7 @@ export function createResource<T extends EventSource, Args extends any[]>( resource ? disposeFn(resource) : Promise.resolve(), promise ? disposeFn(await promise) : Promise.resolve() ]); - } catch (e) { - logError('Failed to dispose the resource', e); - } + } catch (e) {} }; // recreate the current resource diff --git a/packages/sdk/webpack.config.js b/packages/sdk/webpack.config.js index 29750a73..852e5cff 100644 --- a/packages/sdk/webpack.config.js +++ b/packages/sdk/webpack.config.js @@ -1,45 +1,51 @@ const path = require('path'); const webpack = require('webpack'); +const packageJson = require('./package.json'); + +const version = packageJson.version; module.exports = { - entry: './src/index.ts', - module: { - rules: [ - { - test: /\.ts?$/, - use: 'ts-loader', - exclude: ['/node_modules', '/lib'], - }, - { - test: /\.m?js/, - resolve: { - fullySpecified: false - } - } - ] - }, - plugins: [ - new webpack.SourceMapDevToolPlugin({ - test: [/\.ts$/], - exclude: 'vendor', - filename: "app.[hash].js.map", - append: "//# sourceMappingURL=[url]", - moduleFilenameTemplate: '[resource-path]', - fallbackModuleFilenameTemplate: '[resource-path]', - }), - ], - resolve: { - extensions: ['.ts', '.js'], - alias: { - "src": path.resolve(__dirname, 'src'), + entry: './src/index.ts', + module: { + rules: [ + { + test: /\.ts?$/, + use: 'ts-loader', + exclude: ['/node_modules', '/lib'] }, - }, - output: { - filename: 'tonconnect-sdk.min.js', - path: path.resolve(__dirname, 'dist'), - library: 'TonConnectSDK', - clean: true - }, - devtool: 'source-map', - mode: 'production' + { + test: /\.m?js/, + resolve: { + fullySpecified: false + } + } + ] + }, + plugins: [ + new webpack.SourceMapDevToolPlugin({ + test: [/\.ts$/], + exclude: 'vendor', + filename: 'app.[hash].js.map', + append: '//# sourceMappingURL=[url]', + moduleFilenameTemplate: '[resource-path]', + fallbackModuleFilenameTemplate: '[resource-path]' + }), + new webpack.DefinePlugin({ + TON_CONNECT_SDK_VERSION: JSON.stringify(version) + }) + ], + resolve: { + extensions: ['.ts', '.js'], + alias: { + src: path.resolve(__dirname, 'src') + } + }, + output: { + filename: 'tonconnect-sdk.min.js', + path: path.resolve(__dirname, 'dist'), + library: 'TonConnectSDK', + clean: true + }, + devtool: 'source-map', + mode: 'production' };