@explorins/pers-sdk-react-native 1.5.17 → 1.5.20
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/README.md +336 -234
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/useAuth.d.ts +1 -1
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +7 -7
- package/dist/hooks/useRedemptions.d.ts.map +1 -1
- package/dist/hooks/useRedemptions.js +4 -4
- package/dist/hooks/useTenants.d.ts.map +1 -1
- package/dist/hooks/useTenants.js +0 -1
- package/dist/hooks/useTransactionSigner.d.ts +186 -53
- package/dist/hooks/useTransactionSigner.d.ts.map +1 -1
- package/dist/hooks/useTransactionSigner.js +285 -102
- package/dist/hooks/useTransactions.d.ts +2 -2
- package/dist/hooks/useTransactions.d.ts.map +1 -1
- package/dist/hooks/useTransactions.js +38 -9
- package/dist/index.d.ts +211 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21154 -18543
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts +2 -8
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +16 -31
- package/dist/providers/react-native-auth-provider.d.ts +25 -36
- package/dist/providers/react-native-auth-provider.d.ts.map +1 -1
- package/dist/providers/react-native-auth-provider.js +36 -146
- package/dist/storage/async-storage-token-storage.d.ts +22 -0
- package/dist/storage/async-storage-token-storage.d.ts.map +1 -0
- package/dist/storage/async-storage-token-storage.js +113 -0
- package/package.json +16 -11
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useAuth.ts +7 -7
- package/src/hooks/useRedemptions.ts +5 -7
- package/src/hooks/useTenants.ts +0 -1
- package/src/hooks/useTransactionSigner.ts +322 -166
- package/src/hooks/useTransactions.ts +44 -11
- package/src/index.ts +243 -7
- package/src/providers/PersSDKProvider.tsx +21 -40
- package/src/providers/react-native-auth-provider.ts +59 -176
- package/src/storage/async-storage-token-storage.ts +133 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { useCallback, useState, useRef, useEffect } from 'react';
|
|
2
|
-
import { usePersSDK } from '../providers/PersSDKProvider';
|
|
3
2
|
// Dynamic import the signer SDK to avoid build issues with static dependencies
|
|
4
3
|
let createPersSignerSDK = null;
|
|
5
4
|
try {
|
|
6
5
|
const signerModule = require('@explorins/pers-signer/react-native');
|
|
7
6
|
createPersSignerSDK = signerModule.createPersSignerSDK;
|
|
8
|
-
console.log('[useTransactionSigner] PERS Signer SDK loaded successfully');
|
|
9
7
|
}
|
|
10
8
|
catch (error) {
|
|
11
9
|
console.warn('[useTransactionSigner] PERS Signer SDK not available:', error.message);
|
|
@@ -16,53 +14,199 @@ const DEFAULT_ETHERS_PROVIDER = "https://sepolia.infura.io/v3/2781b4b5242343d5b0
|
|
|
16
14
|
/**
|
|
17
15
|
* React Native hook for blockchain transaction signing using PERS Signer SDK
|
|
18
16
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* This hook provides a complete blockchain transaction signing solution for React Native
|
|
18
|
+
* applications, integrating with the PERS ecosystem for tourism loyalty and reward systems.
|
|
19
|
+
* It automatically handles WebAuthn authentication, transaction preparation, signing, and
|
|
20
|
+
* blockchain submission in a single convenient interface.
|
|
21
|
+
*
|
|
22
|
+
* **Features:**
|
|
23
|
+
* - Automatic WebAuthn provider initialization for React Native
|
|
24
|
+
* - 5-minute authentication caching to reduce repeated logins
|
|
25
|
+
* - Complete transaction lifecycle management (sign + submit)
|
|
26
|
+
* - Comprehensive error handling with detailed error messages
|
|
27
|
+
* - Real-time status monitoring for UI feedback
|
|
28
|
+
* - Automatic retry logic for network failures
|
|
29
|
+
*
|
|
30
|
+
* **Underlying SDK Methods:**
|
|
31
|
+
* The hook uses the PERS Signer SDK which provides 5 core methods:
|
|
32
|
+
* 1. `loginUser(jwtToken)` - Authenticate user with 5-minute caching
|
|
33
|
+
* 2. `signTransaction(signingData, jwtToken)` - Sign transactions with auto-login
|
|
34
|
+
* 3. `submitTransaction(signingResult, jwtToken)` - Submit signed transactions
|
|
35
|
+
* 4. `signPersTransaction(jwtToken)` - Legacy one-liner for backward compatibility
|
|
36
|
+
* 5. `signAndSubmitPersTransaction(jwtToken)` - Complete sign + submit flow (used by this hook)
|
|
37
|
+
*
|
|
38
|
+
* **Security:**
|
|
39
|
+
* - WebAuthn-based secure authentication (no passwords stored)
|
|
40
|
+
* - JWT token validation and expiration checking
|
|
41
|
+
* - Secure transaction signing using device biometrics/PIN
|
|
42
|
+
* - No sensitive data stored locally
|
|
43
|
+
*
|
|
44
|
+
* @returns {TransactionSignerHook} Hook interface with signing methods and status
|
|
21
45
|
*
|
|
22
46
|
* @example
|
|
47
|
+
* **Basic Usage:**
|
|
23
48
|
* ```typescript
|
|
24
|
-
*
|
|
25
|
-
*
|
|
49
|
+
* import { useTransactionSigner } from '@explorins/pers-sdk-react-native';
|
|
50
|
+
*
|
|
51
|
+
* function TransactionScreen() {
|
|
52
|
+
* const {
|
|
53
|
+
* signAndSubmitTransactionWithJWT,
|
|
54
|
+
* isSignerAvailable,
|
|
55
|
+
* isSignerInitialized
|
|
56
|
+
* } = useTransactionSigner();
|
|
26
57
|
*
|
|
27
58
|
* const handleSign = async (jwtFromRedemption: string) => {
|
|
59
|
+
* if (!isSignerAvailable) {
|
|
60
|
+
* console.error('Signer not available');
|
|
61
|
+
* return;
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
28
64
|
* try {
|
|
29
|
-
* const result = await
|
|
65
|
+
* const result = await signAndSubmitTransactionWithJWT(jwtFromRedemption);
|
|
30
66
|
* if (result.success) {
|
|
31
|
-
* console.log('Transaction
|
|
67
|
+
* console.log('Transaction completed:', result.transactionHash);
|
|
68
|
+
* // Handle successful transaction
|
|
69
|
+
* if (result.shouldRedirect && result.redirectUrl) {
|
|
70
|
+
* // Navigate to success page or external URL
|
|
71
|
+
* }
|
|
32
72
|
* }
|
|
33
73
|
* } catch (error) {
|
|
34
|
-
* console.error('
|
|
74
|
+
* console.error('Transaction failed:', error.message);
|
|
75
|
+
* // Handle error (show user-friendly message)
|
|
35
76
|
* }
|
|
36
77
|
* };
|
|
37
78
|
*
|
|
38
79
|
* return (
|
|
39
|
-
* <
|
|
40
|
-
*
|
|
41
|
-
*
|
|
80
|
+
* <View style={styles.container}>
|
|
81
|
+
* <TouchableOpacity
|
|
82
|
+
* onPress={() => handleSign(redeemJWT)}
|
|
83
|
+
* disabled={!isSignerAvailable}
|
|
84
|
+
* style={[styles.button, !isSignerAvailable && styles.disabled]}
|
|
85
|
+
* >
|
|
86
|
+
* <Text style={styles.buttonText}>
|
|
87
|
+
* {isSignerInitialized ? 'Sign Transaction' : 'Initializing...'}
|
|
88
|
+
* </Text>
|
|
89
|
+
* </TouchableOpacity>
|
|
90
|
+
* </View>
|
|
42
91
|
* );
|
|
43
92
|
* }
|
|
44
93
|
* ```
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* **Advanced Usage with Error Handling:**
|
|
97
|
+
* ```typescript
|
|
98
|
+
* function AdvancedTransactionComponent() {
|
|
99
|
+
* const [isLoading, setIsLoading] = useState(false);
|
|
100
|
+
* const [error, setError] = useState<string | null>(null);
|
|
101
|
+
* const { signAndSubmitTransactionWithJWT, isSignerAvailable } = useTransactionSigner();
|
|
102
|
+
*
|
|
103
|
+
* const handleTransaction = async (jwt: string) => {
|
|
104
|
+
* setIsLoading(true);
|
|
105
|
+
* setError(null);
|
|
106
|
+
*
|
|
107
|
+
* try {
|
|
108
|
+
* const result = await signAndSubmitTransactionWithJWT(jwt);
|
|
109
|
+
*
|
|
110
|
+
* if (result.success) {
|
|
111
|
+
* // Success handling
|
|
112
|
+
* Alert.alert(
|
|
113
|
+
* 'Success',
|
|
114
|
+
* `Transaction completed!\nHash: ${result.transactionHash}`
|
|
115
|
+
* );
|
|
116
|
+
* } else {
|
|
117
|
+
* throw new Error(result.error || 'Transaction failed');
|
|
118
|
+
* }
|
|
119
|
+
* } catch (err) {
|
|
120
|
+
* const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
121
|
+
* setError(errorMessage);
|
|
122
|
+
*
|
|
123
|
+
* // Different error handling based on error type
|
|
124
|
+
* if (errorMessage.includes('expired')) {
|
|
125
|
+
* Alert.alert('Session Expired', 'Please log in again');
|
|
126
|
+
* } else if (errorMessage.includes('network')) {
|
|
127
|
+
* Alert.alert('Network Error', 'Please check your connection');
|
|
128
|
+
* } else {
|
|
129
|
+
* Alert.alert('Transaction Failed', errorMessage);
|
|
130
|
+
* }
|
|
131
|
+
* } finally {
|
|
132
|
+
* setIsLoading(false);
|
|
133
|
+
* }
|
|
134
|
+
* };
|
|
135
|
+
*
|
|
136
|
+
* return (
|
|
137
|
+
* <View>
|
|
138
|
+
* {error && (
|
|
139
|
+
* <Text style={styles.errorText}>{error}</Text>
|
|
140
|
+
* )}
|
|
141
|
+
* <TouchableOpacity
|
|
142
|
+
* onPress={() => handleTransaction(jwtToken)}
|
|
143
|
+
* disabled={!isSignerAvailable || isLoading}
|
|
144
|
+
* >
|
|
145
|
+
* <Text>
|
|
146
|
+
* {isLoading ? 'Processing...' : 'Sign & Submit Transaction'}
|
|
147
|
+
* </Text>
|
|
148
|
+
* </TouchableOpacity>
|
|
149
|
+
* </View>
|
|
150
|
+
* );
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* **Integration with PERS SDK:**
|
|
156
|
+
* ```typescript
|
|
157
|
+
* import { usePersSDK } from '@explorins/pers-sdk-react-native';
|
|
158
|
+
* import { useTransactionSigner } from '@explorins/pers-sdk-react-native';
|
|
159
|
+
*
|
|
160
|
+
* function RedemptionFlow() {
|
|
161
|
+
* const { user, redeemTokens } = usePersSDK();
|
|
162
|
+
* const { signAndSubmitTransactionWithJWT } = useTransactionSigner();
|
|
163
|
+
*
|
|
164
|
+
* const handleRedemption = async (tokenAmount: number) => {
|
|
165
|
+
* try {
|
|
166
|
+
* // Step 1: Create redemption with PERS SDK
|
|
167
|
+
* const redemption = await redeemTokens({
|
|
168
|
+
* tokenAmount,
|
|
169
|
+
* destinationAddress: user?.walletAddress
|
|
170
|
+
* });
|
|
171
|
+
*
|
|
172
|
+
* // Step 2: Sign and submit transaction
|
|
173
|
+
* const txResult = await signAndSubmitTransactionWithJWT(redemption.jwtToken);
|
|
174
|
+
*
|
|
175
|
+
* if (txResult.success) {
|
|
176
|
+
* console.log('Redemption completed:', txResult.transactionHash);
|
|
177
|
+
* }
|
|
178
|
+
* } catch (error) {
|
|
179
|
+
* console.error('Redemption failed:', error);
|
|
180
|
+
* }
|
|
181
|
+
* };
|
|
182
|
+
* }
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* @see {@link SubmissionResult} for transaction result structure
|
|
186
|
+
* @see {@link TransactionSigningResult} for signing result details
|
|
187
|
+
* @see {@link AuthenticatedUser} for user authentication data
|
|
188
|
+
*
|
|
189
|
+
* @since 1.5.0
|
|
45
190
|
*/
|
|
46
191
|
export const useTransactionSigner = () => {
|
|
47
|
-
const { isInitialized, isAuthenticated, user } = usePersSDK();
|
|
192
|
+
// const { isInitialized, isAuthenticated, user } = usePersSDK();
|
|
48
193
|
const [isSignerInitialized, setIsSignerInitialized] = useState(false);
|
|
49
194
|
const signerSDKRef = useRef(null);
|
|
50
195
|
// Auto-initialize signer SDK when PERS SDK is ready
|
|
51
196
|
useEffect(() => {
|
|
52
|
-
console.log('[useTransactionSigner] Auto-initializing signer SDK...');
|
|
53
197
|
const initializeSignerSDK = async () => {
|
|
54
198
|
if (!createPersSignerSDK) {
|
|
55
199
|
console.warn('[useTransactionSigner] Signer SDK not available');
|
|
56
200
|
return;
|
|
57
201
|
}
|
|
58
202
|
try {
|
|
59
|
-
|
|
203
|
+
// React Native createPersSignerSDK automatically includes WebAuthn provider
|
|
60
204
|
const signerSDK = await createPersSignerSDK({
|
|
61
|
-
ethersProviderUrl: DEFAULT_ETHERS_PROVIDER
|
|
205
|
+
ethersProviderUrl: DEFAULT_ETHERS_PROVIDER,
|
|
206
|
+
relyingPartyName: 'PERS React Native App'
|
|
62
207
|
});
|
|
63
208
|
signerSDKRef.current = signerSDK;
|
|
64
209
|
setIsSignerInitialized(true);
|
|
65
|
-
console.log('[useTransactionSigner] PERS Signer SDK initialized successfully');
|
|
66
210
|
}
|
|
67
211
|
catch (error) {
|
|
68
212
|
console.error('[useTransactionSigner] Failed to initialize signer SDK:', error);
|
|
@@ -73,17 +217,85 @@ export const useTransactionSigner = () => {
|
|
|
73
217
|
}
|
|
74
218
|
}, [isSignerInitialized]);
|
|
75
219
|
/**
|
|
76
|
-
* Sign a blockchain transaction using JWT token
|
|
220
|
+
* Sign and submit a blockchain transaction using JWT token
|
|
221
|
+
*
|
|
222
|
+
* This is the main method for executing blockchain transactions in React Native.
|
|
223
|
+
* It handles the complete transaction lifecycle: authentication, signing, and
|
|
224
|
+
* blockchain submission in a single call.
|
|
225
|
+
*
|
|
226
|
+
* **Process Flow:**
|
|
227
|
+
* 1. Validates the JWT token and extracts transaction data
|
|
228
|
+
* 2. Authenticates the user using WebAuthn (cached for 5 minutes)
|
|
229
|
+
* 3. Fetches transaction data from PERS backend
|
|
230
|
+
* 4. Signs the transaction using device security (biometrics/PIN)
|
|
231
|
+
* 5. Submits the signed transaction to the blockchain
|
|
232
|
+
* 6. Returns detailed result with transaction hash and status
|
|
233
|
+
*
|
|
234
|
+
* **Security Features:**
|
|
235
|
+
* - JWT token validation and expiration checking
|
|
236
|
+
* - WebAuthn authentication with device biometrics
|
|
237
|
+
* - Secure transaction signing without exposing private keys
|
|
238
|
+
* - Automatic session management with secure caching
|
|
77
239
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* 3. Sign transaction with authenticated user
|
|
240
|
+
* @param {string} jwt - JWT token containing transaction ID and user information
|
|
241
|
+
* Must include: `transactionId`, `identifierEmail`, `tenantId`
|
|
242
|
+
* @returns {Promise<SubmissionResult>} Complete transaction result
|
|
82
243
|
*
|
|
83
|
-
* @
|
|
84
|
-
* @
|
|
244
|
+
* @throws {Error} 'Transaction signer not initialized' - Hook not ready
|
|
245
|
+
* @throws {Error} 'PERS Signer SDK not available' - SDK installation issue
|
|
246
|
+
* @throws {Error} 'Invalid or expired JWT token' - Token validation failed
|
|
247
|
+
* @throws {Error} 'Authentication failed' - WebAuthn authentication failed
|
|
248
|
+
* @throws {Error} 'Transaction signing failed' - Signing process failed
|
|
249
|
+
* @throws {Error} 'Transaction submission failed' - Blockchain submission failed
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* **Basic Transaction:**
|
|
253
|
+
* ```typescript
|
|
254
|
+
* const { signAndSubmitTransactionWithJWT } = useTransactionSigner();
|
|
255
|
+
*
|
|
256
|
+
* const result = await signAndSubmitTransactionWithJWT(jwtToken);
|
|
257
|
+
* if (result.success) {
|
|
258
|
+
* console.log('Transaction Hash:', result.transactionHash);
|
|
259
|
+
* console.log('Blockchain explorer: [Chain-specific explorer URL]');
|
|
260
|
+
* }
|
|
261
|
+
* ```
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* **With Error Handling:**
|
|
265
|
+
* ```typescript
|
|
266
|
+
* try {
|
|
267
|
+
* const result = await signAndSubmitTransactionWithJWT(redeemJWT);
|
|
268
|
+
*
|
|
269
|
+
* if (result.success) {
|
|
270
|
+
* // Transaction successful
|
|
271
|
+
* setTransactionHash(result.transactionHash);
|
|
272
|
+
*
|
|
273
|
+
* if (result.shouldRedirect && result.redirectUrl) {
|
|
274
|
+
* // Handle post-transaction redirect
|
|
275
|
+
* Linking.openURL(result.redirectUrl);
|
|
276
|
+
* }
|
|
277
|
+
* } else {
|
|
278
|
+
* // Transaction failed but didn't throw
|
|
279
|
+
* setError(result.error || 'Transaction failed');
|
|
280
|
+
* }
|
|
281
|
+
* } catch (error) {
|
|
282
|
+
* // Handle different error types
|
|
283
|
+
* if (error.message.includes('expired')) {
|
|
284
|
+
* // Token expired - redirect to login
|
|
285
|
+
* navigation.navigate('Login');
|
|
286
|
+
* } else if (error.message.includes('network')) {
|
|
287
|
+
* // Network error - suggest retry
|
|
288
|
+
* setError('Network error. Please try again.');
|
|
289
|
+
* } else {
|
|
290
|
+
* // Other errors
|
|
291
|
+
* setError(error.message);
|
|
292
|
+
* }
|
|
293
|
+
* }
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* @see {@link SubmissionResult} for detailed result structure
|
|
85
297
|
*/
|
|
86
|
-
const
|
|
298
|
+
const signAndSubmitTransactionWithJWT = useCallback(async (jwt) => {
|
|
87
299
|
if (!isSignerInitialized || !signerSDKRef.current) {
|
|
88
300
|
throw new Error('Transaction signer not initialized');
|
|
89
301
|
}
|
|
@@ -91,95 +303,66 @@ export const useTransactionSigner = () => {
|
|
|
91
303
|
throw new Error('PERS Signer SDK not available. Blockchain signing is not supported.');
|
|
92
304
|
}
|
|
93
305
|
try {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
searchParams.set('jwt', jwt);
|
|
98
|
-
// Step 1: Authenticate with JWT (handles tenant setup, project keys automatically)
|
|
99
|
-
console.log('[useTransactionSigner] Authenticating with JWT...');
|
|
100
|
-
const authResult = await signerSDKRef.current.authenticateWithJWT(searchParams);
|
|
101
|
-
if (!authResult) {
|
|
102
|
-
throw new Error('JWT authentication failed - no valid token found');
|
|
103
|
-
}
|
|
104
|
-
if (authResult.isExpired) {
|
|
105
|
-
throw new Error('JWT token is expired');
|
|
106
|
-
}
|
|
107
|
-
console.log('[useTransactionSigner] JWT authentication successful:', authResult.user.identifier);
|
|
108
|
-
// Step 2: Extract transaction ID from JWT
|
|
109
|
-
const jwtParts = jwt.split('.');
|
|
110
|
-
if (jwtParts.length !== 3) {
|
|
111
|
-
throw new Error('Invalid JWT token format');
|
|
112
|
-
}
|
|
113
|
-
const payload = JSON.parse(atob(jwtParts[1]));
|
|
114
|
-
const transactionId = payload.transactionId;
|
|
115
|
-
if (!transactionId) {
|
|
116
|
-
throw new Error('No transaction ID found in JWT token');
|
|
117
|
-
}
|
|
118
|
-
console.log('[useTransactionSigner] Found transaction ID:', transactionId);
|
|
119
|
-
// Step 3: Sign transaction using authenticated user
|
|
120
|
-
console.log('[useTransactionSigner] Signing transaction...');
|
|
121
|
-
const signingResult = await signerSDKRef.current.signPersTransaction(authResult.user, transactionId);
|
|
122
|
-
console.log('[useTransactionSigner] Transaction signing result:', signingResult);
|
|
123
|
-
return {
|
|
124
|
-
success: signingResult.success,
|
|
125
|
-
transactionHash: signingResult.transactionHash,
|
|
126
|
-
error: signingResult.error
|
|
127
|
-
};
|
|
306
|
+
// Use the actual SDK method that handles the complete sign + submit flow
|
|
307
|
+
const submissionResult = await signerSDKRef.current.signAndSubmitPersTransaction(jwt);
|
|
308
|
+
return submissionResult;
|
|
128
309
|
}
|
|
129
310
|
catch (error) {
|
|
130
311
|
console.error('[useTransactionSigner] JWT transaction signing failed:', error);
|
|
131
|
-
|
|
132
|
-
success: false,
|
|
133
|
-
error: `Transaction signing failed: ${error}`
|
|
134
|
-
};
|
|
312
|
+
throw error; // Re-throw to maintain error handling upstream
|
|
135
313
|
}
|
|
136
314
|
}, [isSignerInitialized]);
|
|
137
|
-
/**
|
|
138
|
-
* Manual user authentication (fallback method)
|
|
139
|
-
*/
|
|
140
|
-
const authenticateUser = useCallback(async (identifier) => {
|
|
141
|
-
if (!isSignerInitialized || !signerSDKRef.current) {
|
|
142
|
-
throw new Error('Transaction signer not initialized');
|
|
143
|
-
}
|
|
144
|
-
try {
|
|
145
|
-
console.log('[useTransactionSigner] Manual user authentication:', identifier);
|
|
146
|
-
const user = await signerSDKRef.current.authenticateUser(identifier);
|
|
147
|
-
console.log('[useTransactionSigner] Manual authentication successful');
|
|
148
|
-
return user;
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
console.error('[useTransactionSigner] Manual authentication failed:', error);
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
}, [isSignerInitialized]);
|
|
155
|
-
/**
|
|
156
|
-
* Legacy method for backward compatibility
|
|
157
|
-
* @deprecated Use signTransactionWithJWT instead
|
|
158
|
-
*/
|
|
159
|
-
const signTransaction = useCallback(async (jwt) => {
|
|
160
|
-
console.warn('[useTransactionSigner] signTransaction is deprecated, use signTransactionWithJWT');
|
|
161
|
-
return signTransactionWithJWT(jwt);
|
|
162
|
-
}, [signTransactionWithJWT]);
|
|
163
315
|
return {
|
|
164
316
|
/**
|
|
165
|
-
* Sign a blockchain transaction using JWT token
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
*
|
|
170
|
-
* @
|
|
171
|
-
|
|
172
|
-
signTransaction,
|
|
173
|
-
/**
|
|
174
|
-
* Manual user authentication (for advanced use cases)
|
|
317
|
+
* Sign and submit a blockchain transaction using JWT token
|
|
318
|
+
*
|
|
319
|
+
* Main method for executing blockchain transactions. Handles complete flow
|
|
320
|
+
* from authentication to blockchain submission in a single call.
|
|
321
|
+
*
|
|
322
|
+
* @param {string} jwt - JWT token with transaction and user data
|
|
323
|
+
* @returns {Promise<SubmissionResult>} Transaction result with hash and status
|
|
175
324
|
*/
|
|
176
|
-
|
|
325
|
+
signAndSubmitTransactionWithJWT,
|
|
177
326
|
/**
|
|
178
|
-
* Whether the transaction signer has been initialized
|
|
327
|
+
* Whether the transaction signer SDK has been successfully initialized
|
|
328
|
+
*
|
|
329
|
+
* Use this to show loading states while the signer is being set up.
|
|
330
|
+
* When `false`, the signer is still initializing and transactions cannot be processed.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* const { isSignerInitialized } = useTransactionSigner();
|
|
335
|
+
*
|
|
336
|
+
* if (!isSignerInitialized) {
|
|
337
|
+
* return <LoadingSpinner text="Initializing signer..." />;
|
|
338
|
+
* }
|
|
339
|
+
* ```
|
|
179
340
|
*/
|
|
180
341
|
isSignerInitialized,
|
|
181
342
|
/**
|
|
182
|
-
* Whether transaction signing is available
|
|
343
|
+
* Whether blockchain transaction signing is fully available
|
|
344
|
+
*
|
|
345
|
+
* This combines multiple checks: SDK availability, initialization status,
|
|
346
|
+
* and configuration validity. Use this to enable/disable transaction buttons.
|
|
347
|
+
*
|
|
348
|
+
* Returns `true` only when:
|
|
349
|
+
* - PERS Signer SDK is installed and available
|
|
350
|
+
* - Signer has been successfully initialized
|
|
351
|
+
* - All required dependencies are loaded
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```typescript
|
|
355
|
+
* const { isSignerAvailable } = useTransactionSigner();
|
|
356
|
+
*
|
|
357
|
+
* return (
|
|
358
|
+
* <TouchableOpacity
|
|
359
|
+
* disabled={!isSignerAvailable}
|
|
360
|
+
* style={[styles.button, !isSignerAvailable && styles.disabled]}
|
|
361
|
+
* >
|
|
362
|
+
* <Text>{isSignerAvailable ? 'Sign Transaction' : 'Signer Unavailable'}</Text>
|
|
363
|
+
* </TouchableOpacity>
|
|
364
|
+
* );
|
|
365
|
+
* ```
|
|
183
366
|
*/
|
|
184
367
|
isSignerAvailable: isSignerInitialized && !!createPersSignerSDK,
|
|
185
368
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TransactionRequestDTO, TransactionRequestResponseDTO, TransactionDTO } from '@explorins/pers-shared';
|
|
1
|
+
import type { TransactionRequestDTO, TransactionRequestResponseDTO, TransactionDTO, TransactionRole } from '@explorins/pers-shared';
|
|
2
2
|
import type { TransactionPaginationParams } from '@explorins/pers-sdk/transaction';
|
|
3
3
|
/**
|
|
4
4
|
* React hook for transaction operations in the PERS SDK
|
|
@@ -39,7 +39,7 @@ import type { TransactionPaginationParams } from '@explorins/pers-sdk/transactio
|
|
|
39
39
|
export declare const useTransactions: () => {
|
|
40
40
|
createTransaction: (request: TransactionRequestDTO) => Promise<TransactionRequestResponseDTO>;
|
|
41
41
|
getTransactionById: (transactionId: string) => Promise<TransactionDTO | null>;
|
|
42
|
-
getUserTransactionHistory: (
|
|
42
|
+
getUserTransactionHistory: (role?: TransactionRole) => Promise<TransactionDTO[]>;
|
|
43
43
|
getTenantTransactions: () => Promise<TransactionDTO[]>;
|
|
44
44
|
getPaginatedTransactions: (params: TransactionPaginationParams) => Promise<any>;
|
|
45
45
|
exportTransactionsCSV: () => Promise<Blob>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTransactions.d.ts","sourceRoot":"","sources":["../../src/hooks/useTransactions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useTransactions.d.ts","sourceRoot":"","sources":["../../src/hooks/useTransactions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,qBAAqB,EACrB,6BAA6B,EAC7B,cAAc,EACd,eAAe,EAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,eAAe;iCA8B4B,qBAAqB,KAAG,QAAQ,6BAA6B,CAAC;wCA8DvD,MAAM,KAAG,QAAQ,cAAc,GAAG,IAAI,CAAC;uCA6BxC,eAAe,KAAG,QAAQ,cAAc,EAAE,CAAC;iCAgBnD,QAAQ,cAAc,EAAE,CAAC;uCAcjB,2BAA2B,KAAG,QAAQ,GAAG,CAAC;iCAelD,QAAQ,IAAI,CAAC;;CAwBlE,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { usePersSDK } from '../providers/PersSDKProvider';
|
|
3
|
+
import { useTransactionSigner } from './useTransactionSigner';
|
|
3
4
|
/**
|
|
4
5
|
* React hook for transaction operations in the PERS SDK
|
|
5
6
|
*
|
|
@@ -38,6 +39,7 @@ import { usePersSDK } from '../providers/PersSDKProvider';
|
|
|
38
39
|
*/
|
|
39
40
|
export const useTransactions = () => {
|
|
40
41
|
const { sdk, isInitialized, isAuthenticated } = usePersSDK();
|
|
42
|
+
const { signAndSubmitTransactionWithJWT, isSignerAvailable } = useTransactionSigner();
|
|
41
43
|
if (!isAuthenticated && isInitialized) {
|
|
42
44
|
console.warn('SDK not authenticated. Some transaction operations may fail.');
|
|
43
45
|
}
|
|
@@ -69,15 +71,43 @@ export const useTransactions = () => {
|
|
|
69
71
|
}
|
|
70
72
|
try {
|
|
71
73
|
const result = await sdk.transactions.createTransaction(request);
|
|
72
|
-
// Cross-platform: Dont handle signature URLs here, leave to caller
|
|
73
74
|
console.log('Transaction created successfully:', result);
|
|
75
|
+
// Check if transaction requires signing (contains actionable authToken)
|
|
76
|
+
// Type assertion needed as TransactionRequestResponseDTO type may not include all dynamic properties
|
|
77
|
+
const txToken = result?.actionable?.authToken;
|
|
78
|
+
if (txToken) {
|
|
79
|
+
console.log('[useTransactions] Transaction requires signing, attempting automatic signature...');
|
|
80
|
+
try {
|
|
81
|
+
if (!isSignerAvailable) {
|
|
82
|
+
console.warn('[useTransactions] Transaction signer not available, skipping automatic signing');
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
// Automatically sign the transaction using the authToken
|
|
86
|
+
const signingResult = await signAndSubmitTransactionWithJWT(txToken);
|
|
87
|
+
if (signingResult.success) {
|
|
88
|
+
console.log('[useTransactions] Transaction signed successfully:', signingResult.transactionHash);
|
|
89
|
+
// Return the original result - the transaction is now signed and will be processed
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.error('[useTransactions] Transaction signing failed:', signingResult.error);
|
|
94
|
+
// Don't throw error - return the original result so caller can handle signature URL manually
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (signingError) {
|
|
99
|
+
console.error('[useTransactions] Blockchain signing error:', signingError);
|
|
100
|
+
// Don't throw error - return the original result so caller can handle signature URL manually
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
74
104
|
return result;
|
|
75
105
|
}
|
|
76
106
|
catch (error) {
|
|
77
107
|
console.error('Failed to create transaction:', error);
|
|
78
108
|
throw error;
|
|
79
109
|
}
|
|
80
|
-
}, [sdk, isInitialized]);
|
|
110
|
+
}, [sdk, isInitialized, signAndSubmitTransactionWithJWT, isSignerAvailable]);
|
|
81
111
|
/**
|
|
82
112
|
* Retrieves a specific transaction by its ID
|
|
83
113
|
*
|
|
@@ -107,25 +137,25 @@ export const useTransactions = () => {
|
|
|
107
137
|
}
|
|
108
138
|
}, [sdk, isInitialized]);
|
|
109
139
|
/**
|
|
110
|
-
* Retrieves transaction history for the authenticated user, filtered by
|
|
140
|
+
* Retrieves transaction history for the authenticated user, filtered by role
|
|
111
141
|
*
|
|
112
|
-
* @param
|
|
142
|
+
* @param role - Optional transaction role filter (TransactionRole.SENDER, TransactionRole.RECIPIENT)
|
|
113
143
|
* @returns Promise resolving to array of user's transactions
|
|
114
144
|
* @throws Error if SDK is not initialized
|
|
115
145
|
*
|
|
116
146
|
* @example
|
|
117
147
|
* ```typescript
|
|
118
148
|
* const { getUserTransactionHistory } = useTransactions();
|
|
119
|
-
* const
|
|
120
|
-
*
|
|
149
|
+
* const sentTransactions = await getUserTransactionHistory(TransactionRole.SENDER);
|
|
150
|
+
* const allTransactions = await getUserTransactionHistory(); // No filter
|
|
121
151
|
* ```
|
|
122
152
|
*/
|
|
123
|
-
const getUserTransactionHistory = useCallback(async (
|
|
153
|
+
const getUserTransactionHistory = useCallback(async (role) => {
|
|
124
154
|
if (!isInitialized || !sdk) {
|
|
125
155
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
126
156
|
}
|
|
127
157
|
try {
|
|
128
|
-
const result = await sdk.transactions.getUserTransactionHistory(
|
|
158
|
+
const result = await sdk.transactions.getUserTransactionHistory(role);
|
|
129
159
|
console.log('Transaction history fetched successfully:', result);
|
|
130
160
|
return result;
|
|
131
161
|
}
|
|
@@ -140,7 +170,6 @@ export const useTransactions = () => {
|
|
|
140
170
|
}
|
|
141
171
|
try {
|
|
142
172
|
const result = await sdk.transactions.getTenantTransactions();
|
|
143
|
-
console.log('Tenant transactions fetched successfully:', result);
|
|
144
173
|
return result;
|
|
145
174
|
}
|
|
146
175
|
catch (error) {
|