@blazium/ton-connect-mobile 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/protocol.d.ts +2 -2
- package/dist/core/protocol.js +16 -11
- package/dist/core/wallets.d.ts +4 -0
- package/dist/core/wallets.js +8 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +188 -4
- package/dist/react/TonConnectButton.d.ts +22 -0
- package/dist/react/TonConnectButton.js +101 -0
- package/dist/react/TonConnectUIProvider.d.ts +110 -0
- package/dist/react/TonConnectUIProvider.js +209 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.js +15 -0
- package/dist/types/index.d.ts +4 -2
- package/package.json +12 -1
- package/src/core/protocol.ts +22 -11
- package/src/core/wallets.ts +12 -0
- package/src/index.ts +226 -5
- package/src/react/TonConnectButton.tsx +103 -0
- package/src/react/TonConnectUIProvider.tsx +290 -0
- package/src/react/index.ts +24 -0
- package/src/types/index.ts +4 -4
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TonConnectButton component
|
|
3
|
+
* Compatible with @tonconnect/ui-react TonConnectButton
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useState } from 'react';
|
|
7
|
+
import { TouchableOpacity, Text, StyleSheet, ActivityIndicator, ViewStyle, TextStyle } from 'react-native';
|
|
8
|
+
import { useTonConnectUI, useTonWallet } from './index';
|
|
9
|
+
|
|
10
|
+
export interface TonConnectButtonProps {
|
|
11
|
+
/** Button text when disconnected */
|
|
12
|
+
text?: string;
|
|
13
|
+
/** Button text when connected */
|
|
14
|
+
connectedText?: string;
|
|
15
|
+
/** Custom styles */
|
|
16
|
+
style?: ViewStyle;
|
|
17
|
+
/** Custom text styles */
|
|
18
|
+
textStyle?: TextStyle;
|
|
19
|
+
/** Callback when button is pressed */
|
|
20
|
+
onPress?: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* TonConnectButton - Button component for connecting/disconnecting wallet
|
|
25
|
+
* Compatible with @tonconnect/ui-react TonConnectButton
|
|
26
|
+
*/
|
|
27
|
+
export function TonConnectButton({
|
|
28
|
+
text = 'Connect Wallet',
|
|
29
|
+
connectedText = 'Disconnect',
|
|
30
|
+
style,
|
|
31
|
+
textStyle,
|
|
32
|
+
onPress,
|
|
33
|
+
}: TonConnectButtonProps): JSX.Element {
|
|
34
|
+
const tonConnectUI = useTonConnectUI();
|
|
35
|
+
const wallet = useTonWallet();
|
|
36
|
+
const isConnected = wallet?.connected || false;
|
|
37
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
38
|
+
|
|
39
|
+
const handlePress = async () => {
|
|
40
|
+
// CRITICAL FIX: Prevent multiple simultaneous presses
|
|
41
|
+
if (isLoading) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (onPress) {
|
|
46
|
+
onPress();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setIsLoading(true);
|
|
51
|
+
try {
|
|
52
|
+
if (isConnected) {
|
|
53
|
+
await tonConnectUI.disconnect();
|
|
54
|
+
} else {
|
|
55
|
+
await tonConnectUI.openModal();
|
|
56
|
+
await tonConnectUI.connectWallet();
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
// CRITICAL FIX: Handle errors gracefully
|
|
60
|
+
console.error('TonConnectButton error:', error);
|
|
61
|
+
// Error is already handled by the SDK/UI, just reset loading state
|
|
62
|
+
} finally {
|
|
63
|
+
setIsLoading(false);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<TouchableOpacity
|
|
69
|
+
style={[styles.button, style, isLoading && styles.buttonDisabled]}
|
|
70
|
+
onPress={handlePress}
|
|
71
|
+
disabled={isLoading}
|
|
72
|
+
>
|
|
73
|
+
{isLoading ? (
|
|
74
|
+
<ActivityIndicator color="#ffffff" />
|
|
75
|
+
) : (
|
|
76
|
+
<Text style={[styles.buttonText, textStyle]}>
|
|
77
|
+
{isConnected ? connectedText : text}
|
|
78
|
+
</Text>
|
|
79
|
+
)}
|
|
80
|
+
</TouchableOpacity>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const styles = StyleSheet.create({
|
|
85
|
+
button: {
|
|
86
|
+
backgroundColor: '#0088cc',
|
|
87
|
+
paddingHorizontal: 24,
|
|
88
|
+
paddingVertical: 12,
|
|
89
|
+
borderRadius: 8,
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
justifyContent: 'center',
|
|
92
|
+
minHeight: 44,
|
|
93
|
+
},
|
|
94
|
+
buttonDisabled: {
|
|
95
|
+
opacity: 0.6,
|
|
96
|
+
},
|
|
97
|
+
buttonText: {
|
|
98
|
+
color: '#ffffff',
|
|
99
|
+
fontSize: 16,
|
|
100
|
+
fontWeight: '600',
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React integration layer for @tonconnect/ui-react compatibility
|
|
3
|
+
* Provides TonConnectUIProvider, hooks, and components compatible with @tonconnect/ui-react API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react';
|
|
7
|
+
import { TonConnectMobile, ConnectionStatus, WalletInfo, SendTransactionRequest } from '../index';
|
|
8
|
+
import type { TonConnectMobileConfig } from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Account information (compatible with @tonconnect/ui-react)
|
|
12
|
+
*/
|
|
13
|
+
export interface Account {
|
|
14
|
+
address: string;
|
|
15
|
+
chain: number;
|
|
16
|
+
publicKey?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wallet state (compatible with @tonconnect/ui-react)
|
|
21
|
+
*/
|
|
22
|
+
export interface WalletState {
|
|
23
|
+
account: Account | null;
|
|
24
|
+
wallet: WalletInfo | null;
|
|
25
|
+
connected: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Transaction response (compatible with @tonconnect/ui-react)
|
|
30
|
+
*/
|
|
31
|
+
export interface TransactionResponse {
|
|
32
|
+
boc: string;
|
|
33
|
+
signature: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Sign data request
|
|
38
|
+
*/
|
|
39
|
+
export interface SignDataRequest {
|
|
40
|
+
/** Data to sign (will be base64 encoded) */
|
|
41
|
+
data: string | Uint8Array;
|
|
42
|
+
/** Optional version */
|
|
43
|
+
version?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Sign data response
|
|
48
|
+
*/
|
|
49
|
+
export interface SignDataResponse {
|
|
50
|
+
signature: string;
|
|
51
|
+
timestamp: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* TonConnect UI instance interface (compatible with @tonconnect/ui-react)
|
|
56
|
+
*/
|
|
57
|
+
export interface TonConnectUI {
|
|
58
|
+
/** Open connection modal */
|
|
59
|
+
openModal: () => Promise<void>;
|
|
60
|
+
/** Close connection modal */
|
|
61
|
+
closeModal: () => void;
|
|
62
|
+
/** Connect to wallet */
|
|
63
|
+
connectWallet: () => Promise<void>;
|
|
64
|
+
/** Disconnect from wallet */
|
|
65
|
+
disconnect: () => Promise<void>;
|
|
66
|
+
/** Send transaction */
|
|
67
|
+
sendTransaction: (transaction: SendTransactionRequest) => Promise<TransactionResponse>;
|
|
68
|
+
/** Sign data */
|
|
69
|
+
signData: (request: SignDataRequest) => Promise<SignDataResponse>;
|
|
70
|
+
/** Current wallet state */
|
|
71
|
+
wallet: WalletState | null;
|
|
72
|
+
/** Modal open state */
|
|
73
|
+
modalState: {
|
|
74
|
+
open: boolean;
|
|
75
|
+
};
|
|
76
|
+
/** UI kit version */
|
|
77
|
+
uiVersion: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Context value
|
|
82
|
+
*/
|
|
83
|
+
interface TonConnectUIContextValue {
|
|
84
|
+
tonConnectUI: TonConnectUI;
|
|
85
|
+
sdk: TonConnectMobile;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const TonConnectUIContext = createContext<TonConnectUIContextValue | null>(null);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* TonConnectUIProvider props
|
|
92
|
+
*/
|
|
93
|
+
export interface TonConnectUIProviderProps {
|
|
94
|
+
/** SDK configuration */
|
|
95
|
+
config: TonConnectMobileConfig;
|
|
96
|
+
/** Children */
|
|
97
|
+
children: ReactNode;
|
|
98
|
+
/** Optional SDK instance (for testing or custom instances) */
|
|
99
|
+
sdkInstance?: TonConnectMobile;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* TonConnectUIProvider - React context provider for TON Connect
|
|
104
|
+
* Compatible with @tonconnect/ui-react API
|
|
105
|
+
*/
|
|
106
|
+
export function TonConnectUIProvider({
|
|
107
|
+
config,
|
|
108
|
+
children,
|
|
109
|
+
sdkInstance,
|
|
110
|
+
}: TonConnectUIProviderProps): JSX.Element {
|
|
111
|
+
const [sdk] = useState<TonConnectMobile>(() => sdkInstance || new TonConnectMobile(config));
|
|
112
|
+
const [walletState, setWalletState] = useState<WalletState | null>(null);
|
|
113
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
114
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
115
|
+
|
|
116
|
+
// Update wallet state from SDK status
|
|
117
|
+
const updateWalletState = useCallback((status: ConnectionStatus) => {
|
|
118
|
+
if (status.connected && status.wallet) {
|
|
119
|
+
setWalletState({
|
|
120
|
+
account: {
|
|
121
|
+
address: status.wallet.address,
|
|
122
|
+
chain: -239, // TON mainnet chain ID
|
|
123
|
+
publicKey: status.wallet.publicKey,
|
|
124
|
+
},
|
|
125
|
+
wallet: status.wallet,
|
|
126
|
+
connected: true,
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
setWalletState({
|
|
130
|
+
account: null,
|
|
131
|
+
wallet: null,
|
|
132
|
+
connected: false,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
// Subscribe to SDK status changes
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
// Set initial state
|
|
140
|
+
const initialStatus = sdk.getStatus();
|
|
141
|
+
updateWalletState(initialStatus);
|
|
142
|
+
|
|
143
|
+
// Subscribe to changes
|
|
144
|
+
const unsubscribe = sdk.onStatusChange((status) => {
|
|
145
|
+
updateWalletState(status);
|
|
146
|
+
// Close modal when connected
|
|
147
|
+
if (status.connected) {
|
|
148
|
+
setModalOpen(false);
|
|
149
|
+
setIsConnecting(false);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return () => {
|
|
154
|
+
unsubscribe();
|
|
155
|
+
// CRITICAL FIX: Cleanup SDK on unmount to prevent memory leaks
|
|
156
|
+
// Note: SDK has its own cleanup via destroy(), but we don't call it here
|
|
157
|
+
// to allow SDK to persist across component remounts (e.g., navigation)
|
|
158
|
+
};
|
|
159
|
+
}, [sdk, updateWalletState]);
|
|
160
|
+
|
|
161
|
+
// Open modal
|
|
162
|
+
const openModal = useCallback(async () => {
|
|
163
|
+
setModalOpen(true);
|
|
164
|
+
}, []);
|
|
165
|
+
|
|
166
|
+
// Close modal
|
|
167
|
+
const closeModal = useCallback(() => {
|
|
168
|
+
setModalOpen(false);
|
|
169
|
+
setIsConnecting(false);
|
|
170
|
+
}, []);
|
|
171
|
+
|
|
172
|
+
// Connect wallet
|
|
173
|
+
const connectWallet = useCallback(async () => {
|
|
174
|
+
// CRITICAL FIX: Use functional update to avoid race condition
|
|
175
|
+
setIsConnecting((prev) => {
|
|
176
|
+
if (prev) {
|
|
177
|
+
// Already connecting, return early
|
|
178
|
+
return prev;
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Wait for connection
|
|
184
|
+
try {
|
|
185
|
+
await sdk.connect();
|
|
186
|
+
// Status update will be handled by the subscription
|
|
187
|
+
} catch (error) {
|
|
188
|
+
setIsConnecting(false);
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}, [sdk]);
|
|
192
|
+
|
|
193
|
+
// Disconnect
|
|
194
|
+
const disconnect = useCallback(async () => {
|
|
195
|
+
await sdk.disconnect();
|
|
196
|
+
setModalOpen(false);
|
|
197
|
+
}, [sdk]);
|
|
198
|
+
|
|
199
|
+
// Send transaction
|
|
200
|
+
const sendTransaction = useCallback(
|
|
201
|
+
async (transaction: SendTransactionRequest): Promise<TransactionResponse> => {
|
|
202
|
+
const response = await sdk.sendTransaction(transaction);
|
|
203
|
+
return {
|
|
204
|
+
boc: response.boc,
|
|
205
|
+
signature: response.signature,
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
[sdk]
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// Sign data
|
|
212
|
+
const signData = useCallback(
|
|
213
|
+
async (request: SignDataRequest): Promise<SignDataResponse> => {
|
|
214
|
+
const response = await sdk.signData(request.data, request.version);
|
|
215
|
+
return {
|
|
216
|
+
signature: response.signature,
|
|
217
|
+
timestamp: response.timestamp,
|
|
218
|
+
};
|
|
219
|
+
},
|
|
220
|
+
[sdk]
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// Create TonConnectUI instance
|
|
224
|
+
const tonConnectUI: TonConnectUI = {
|
|
225
|
+
openModal,
|
|
226
|
+
closeModal,
|
|
227
|
+
connectWallet,
|
|
228
|
+
disconnect,
|
|
229
|
+
sendTransaction,
|
|
230
|
+
signData,
|
|
231
|
+
wallet: walletState,
|
|
232
|
+
modalState: {
|
|
233
|
+
open: modalOpen,
|
|
234
|
+
},
|
|
235
|
+
uiVersion: '1.0.0',
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const contextValue: TonConnectUIContextValue = {
|
|
239
|
+
tonConnectUI,
|
|
240
|
+
sdk,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return <TonConnectUIContext.Provider value={contextValue}>{children}</TonConnectUIContext.Provider>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Hook to access TonConnectUI instance
|
|
248
|
+
* Compatible with @tonconnect/ui-react useTonConnectUI hook
|
|
249
|
+
*/
|
|
250
|
+
export function useTonConnectUI(): TonConnectUI {
|
|
251
|
+
const context = useContext(TonConnectUIContext);
|
|
252
|
+
if (!context) {
|
|
253
|
+
throw new Error('useTonConnectUI must be used within TonConnectUIProvider');
|
|
254
|
+
}
|
|
255
|
+
return context.tonConnectUI;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Hook to access wallet state
|
|
260
|
+
* Compatible with @tonconnect/ui-react useTonWallet hook
|
|
261
|
+
*/
|
|
262
|
+
export function useTonWallet(): WalletState | null {
|
|
263
|
+
const tonConnectUI = useTonConnectUI();
|
|
264
|
+
return tonConnectUI.wallet;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Hook to access modal state
|
|
269
|
+
* Compatible with @tonconnect/ui-react useTonConnectModal hook
|
|
270
|
+
*/
|
|
271
|
+
export function useTonConnectModal(): { open: boolean; close: () => void; openModal: () => Promise<void> } {
|
|
272
|
+
const tonConnectUI = useTonConnectUI();
|
|
273
|
+
return {
|
|
274
|
+
open: tonConnectUI.modalState.open,
|
|
275
|
+
close: tonConnectUI.closeModal,
|
|
276
|
+
openModal: tonConnectUI.openModal,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Hook to access SDK instance (for advanced usage)
|
|
282
|
+
*/
|
|
283
|
+
export function useTonConnectSDK(): TonConnectMobile {
|
|
284
|
+
const context = useContext(TonConnectUIContext);
|
|
285
|
+
if (!context) {
|
|
286
|
+
throw new Error('useTonConnectSDK must be used within TonConnectUIProvider');
|
|
287
|
+
}
|
|
288
|
+
return context.sdk;
|
|
289
|
+
}
|
|
290
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React integration exports
|
|
3
|
+
* Re-export all React components and hooks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
TonConnectUIProvider,
|
|
8
|
+
useTonConnectUI,
|
|
9
|
+
useTonWallet,
|
|
10
|
+
useTonConnectModal,
|
|
11
|
+
useTonConnectSDK,
|
|
12
|
+
} from './TonConnectUIProvider';
|
|
13
|
+
export type {
|
|
14
|
+
TonConnectUIProviderProps,
|
|
15
|
+
TonConnectUI,
|
|
16
|
+
WalletState,
|
|
17
|
+
Account,
|
|
18
|
+
TransactionResponse,
|
|
19
|
+
SignDataRequest,
|
|
20
|
+
SignDataResponse,
|
|
21
|
+
} from './TonConnectUIProvider';
|
|
22
|
+
export { TonConnectButton } from './TonConnectButton';
|
|
23
|
+
export type { TonConnectButtonProps } from './TonConnectButton';
|
|
24
|
+
|
package/src/types/index.ts
CHANGED
|
@@ -82,9 +82,9 @@ export interface ConnectionRequestPayload {
|
|
|
82
82
|
name: 'ton_addr';
|
|
83
83
|
}>;
|
|
84
84
|
/** Return strategy - how wallet should return to the app */
|
|
85
|
-
returnStrategy?: 'back' | 'none';
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
returnStrategy?: 'back' | 'post_redirect' | 'none';
|
|
86
|
+
/** Return scheme for mobile apps (required by many wallets for proper callback handling) */
|
|
87
|
+
returnScheme?: string;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
@@ -143,7 +143,7 @@ export interface TransactionRequestPayload {
|
|
|
143
143
|
/** Return URL scheme (for mobile apps) */
|
|
144
144
|
returnScheme?: string;
|
|
145
145
|
/** Return strategy */
|
|
146
|
-
returnStrategy?: 'back' | 'none';
|
|
146
|
+
returnStrategy?: 'back' | 'post_redirect' | 'none';
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|