@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,19 +1,17 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { usePersSDK } from '../providers/PersSDKProvider';
|
|
3
|
-
import { useTransactionSigner } from './useTransactionSigner';
|
|
4
|
-
import type { TransactionSigningResult } from './useTransactionSigner';
|
|
3
|
+
import { SubmissionResult, useTransactionSigner } from './useTransactionSigner';
|
|
5
4
|
import type {
|
|
6
5
|
RedemptionCreateRequestDTO,
|
|
7
6
|
RedemptionDTO,
|
|
8
7
|
RedemptionRedeemDTO,
|
|
9
8
|
RedemptionRedeemRequestResponseDTO,
|
|
10
9
|
RedemptionTypeDTO,
|
|
11
|
-
TokenUnitCreateRequestDTO
|
|
12
10
|
} from '@explorins/pers-shared';
|
|
13
11
|
|
|
14
12
|
export const useRedemptions = () => {
|
|
15
13
|
const { sdk, isInitialized, isAuthenticated } = usePersSDK();
|
|
16
|
-
const {
|
|
14
|
+
const { signAndSubmitTransactionWithJWT, isSignerAvailable } = useTransactionSigner();
|
|
17
15
|
|
|
18
16
|
const getActiveRedemptions = useCallback(async (): Promise<RedemptionDTO[]> => {
|
|
19
17
|
if (!isInitialized || !sdk) {
|
|
@@ -70,7 +68,7 @@ export const useRedemptions = () => {
|
|
|
70
68
|
console.log('Transaction requires blockchain signing, processing with WebAuthn signer...');
|
|
71
69
|
|
|
72
70
|
try {
|
|
73
|
-
const signingResult:
|
|
71
|
+
const signingResult: SubmissionResult = await signAndSubmitTransactionWithJWT(txToken);
|
|
74
72
|
console.log('Blockchain signing result:', signingResult);
|
|
75
73
|
if (signingResult.success) {
|
|
76
74
|
console.log('Transaction signed successfully:', signingResult.transactionHash);
|
|
@@ -78,7 +76,7 @@ export const useRedemptions = () => {
|
|
|
78
76
|
return {
|
|
79
77
|
...result,
|
|
80
78
|
transactionHash: signingResult.transactionHash,
|
|
81
|
-
signature: signingResult.signature,
|
|
79
|
+
// signature: signingResult.signature,
|
|
82
80
|
isSigned: true,
|
|
83
81
|
signedAt: new Date().toISOString()
|
|
84
82
|
} as RedemptionRedeemRequestResponseDTO;
|
|
@@ -101,7 +99,7 @@ export const useRedemptions = () => {
|
|
|
101
99
|
console.error('Failed to redeem redemption:', error);
|
|
102
100
|
throw error;
|
|
103
101
|
}
|
|
104
|
-
}, [sdk, isInitialized, isAuthenticated,
|
|
102
|
+
}, [sdk, isInitialized, isAuthenticated, signAndSubmitTransactionWithJWT, isSignerAvailable]);
|
|
105
103
|
|
|
106
104
|
// Admin methods
|
|
107
105
|
const createRedemption = useCallback(async (redemptionData: RedemptionCreateRequestDTO): Promise<RedemptionDTO> => {
|
package/src/hooks/useTenants.ts
CHANGED
|
@@ -1,53 +1,40 @@
|
|
|
1
1
|
import { useCallback, useState, useRef, useEffect } from 'react';
|
|
2
|
-
import { usePersSDK } from '../providers/PersSDKProvider';
|
|
2
|
+
// import { usePersSDK } from '../providers/PersSDKProvider';
|
|
3
|
+
import { PersSignerConfig } from '@explorins/pers-signer';
|
|
4
|
+
|
|
5
|
+
// Import actual SDK types from the React Native-specific export
|
|
6
|
+
type PersSignerSDK = import('@explorins/pers-signer/react-native').PersSignerSDK;
|
|
7
|
+
type SubmissionResult = import('@explorins/pers-signer/react-native').SubmissionResult;
|
|
8
|
+
type AuthenticatedUser = import('@explorins/pers-signer/react-native').AuthenticatedUser;
|
|
9
|
+
type TransactionSigningResult = import('@explorins/pers-signer/react-native').TransactionSigningResult;
|
|
10
|
+
|
|
3
11
|
|
|
4
12
|
// Dynamic import the signer SDK to avoid build issues with static dependencies
|
|
5
|
-
let createPersSignerSDK: ((config: PersSignerConfig) => Promise<
|
|
13
|
+
let createPersSignerSDK: ((config: PersSignerConfig) => Promise<PersSignerSDK>) | null = null;
|
|
6
14
|
try {
|
|
7
15
|
const signerModule = require('@explorins/pers-signer/react-native');
|
|
8
16
|
createPersSignerSDK = signerModule.createPersSignerSDK;
|
|
9
|
-
console.log('[useTransactionSigner] PERS Signer SDK loaded successfully');
|
|
10
17
|
} catch (error: unknown) {
|
|
11
18
|
console.warn('[useTransactionSigner] PERS Signer SDK not available:', (error as Error).message);
|
|
12
19
|
console.warn('[useTransactionSigner] Real blockchain signing will not be available');
|
|
13
20
|
}
|
|
14
21
|
|
|
15
|
-
// Simplified configuration matching web frontend
|
|
16
|
-
interface PersSignerConfig {
|
|
17
|
-
ethersProviderUrl?: string; // Custom blockchain provider
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Simplified SDK interface matching web frontend
|
|
21
|
-
interface PersSignerSDKInstance {
|
|
22
|
-
authenticateWithJWT: (searchParams: URLSearchParams) => Promise<AuthenticationResult | null>;
|
|
23
|
-
authenticateUser: (identifier: string) => Promise<SignerAuthenticatedUser>;
|
|
24
|
-
signPersTransaction: (user: SignerAuthenticatedUser, transactionId: string) => Promise<SigningResult>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Types matching web frontend
|
|
28
|
-
interface AuthenticationResult {
|
|
29
|
-
user: SignerAuthenticatedUser;
|
|
30
|
-
isExpired: boolean;
|
|
31
|
-
}
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
interface
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
transactionHash?: string;
|
|
49
|
-
signature?: string;
|
|
50
|
-
error?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Return interface for the useTransactionSigner hook
|
|
25
|
+
*
|
|
26
|
+
* Provides secure transaction signing capabilities for EVM blockchain interactions
|
|
27
|
+
* and error handling for React Native applications.
|
|
28
|
+
*
|
|
29
|
+
* @interface TransactionSignerHook
|
|
30
|
+
* @property {Function} signAndSubmitTransactionWithJWT - Main method to sign and submit transactions
|
|
31
|
+
* @property {boolean} isSignerInitialized - Whether the signer SDK has been initialized
|
|
32
|
+
* @property {boolean} isSignerAvailable - Whether signing functionality is fully available
|
|
33
|
+
*/
|
|
34
|
+
interface TransactionSignerHook {
|
|
35
|
+
signAndSubmitTransactionWithJWT: (jwt: string) => Promise<SubmissionResult>;
|
|
36
|
+
isSignerInitialized: boolean;
|
|
37
|
+
isSignerAvailable: boolean;
|
|
51
38
|
}
|
|
52
39
|
|
|
53
40
|
// Constants - TODO: Move to environment config later
|
|
@@ -56,42 +43,187 @@ const DEFAULT_ETHERS_PROVIDER = "https://sepolia.infura.io/v3/2781b4b5242343d5b0
|
|
|
56
43
|
/**
|
|
57
44
|
* React Native hook for blockchain transaction signing using PERS Signer SDK
|
|
58
45
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
46
|
+
* This hook provides a complete blockchain transaction signing solution for React Native
|
|
47
|
+
* applications, integrating with the PERS ecosystem for tourism loyalty and reward systems.
|
|
48
|
+
* It automatically handles WebAuthn authentication, transaction preparation, signing, and
|
|
49
|
+
* blockchain submission in a single convenient interface.
|
|
50
|
+
*
|
|
51
|
+
* **Features:**
|
|
52
|
+
* - Automatic WebAuthn provider initialization for React Native
|
|
53
|
+
* - 5-minute authentication caching to reduce repeated logins
|
|
54
|
+
* - Complete transaction lifecycle management (sign + submit)
|
|
55
|
+
* - Comprehensive error handling with detailed error messages
|
|
56
|
+
* - Real-time status monitoring for UI feedback
|
|
57
|
+
* - Automatic retry logic for network failures
|
|
58
|
+
*
|
|
59
|
+
* **Underlying SDK Methods:**
|
|
60
|
+
* The hook uses the PERS Signer SDK which provides 5 core methods:
|
|
61
|
+
* 1. `loginUser(jwtToken)` - Authenticate user with 5-minute caching
|
|
62
|
+
* 2. `signTransaction(signingData, jwtToken)` - Sign transactions with auto-login
|
|
63
|
+
* 3. `submitTransaction(signingResult, jwtToken)` - Submit signed transactions
|
|
64
|
+
* 4. `signPersTransaction(jwtToken)` - Legacy one-liner for backward compatibility
|
|
65
|
+
* 5. `signAndSubmitPersTransaction(jwtToken)` - Complete sign + submit flow (used by this hook)
|
|
66
|
+
*
|
|
67
|
+
* **Security:**
|
|
68
|
+
* - WebAuthn-based secure authentication (no passwords stored)
|
|
69
|
+
* - JWT token validation and expiration checking
|
|
70
|
+
* - Secure transaction signing using device biometrics/PIN
|
|
71
|
+
* - No sensitive data stored locally
|
|
72
|
+
*
|
|
73
|
+
* @returns {TransactionSignerHook} Hook interface with signing methods and status
|
|
61
74
|
*
|
|
62
75
|
* @example
|
|
76
|
+
* **Basic Usage:**
|
|
63
77
|
* ```typescript
|
|
64
|
-
*
|
|
65
|
-
*
|
|
78
|
+
* import { useTransactionSigner } from '@explorins/pers-sdk-react-native';
|
|
79
|
+
*
|
|
80
|
+
* function TransactionScreen() {
|
|
81
|
+
* const {
|
|
82
|
+
* signAndSubmitTransactionWithJWT,
|
|
83
|
+
* isSignerAvailable,
|
|
84
|
+
* isSignerInitialized
|
|
85
|
+
* } = useTransactionSigner();
|
|
66
86
|
*
|
|
67
87
|
* const handleSign = async (jwtFromRedemption: string) => {
|
|
88
|
+
* if (!isSignerAvailable) {
|
|
89
|
+
* console.error('Signer not available');
|
|
90
|
+
* return;
|
|
91
|
+
* }
|
|
92
|
+
*
|
|
68
93
|
* try {
|
|
69
|
-
* const result = await
|
|
94
|
+
* const result = await signAndSubmitTransactionWithJWT(jwtFromRedemption);
|
|
70
95
|
* if (result.success) {
|
|
71
|
-
* console.log('Transaction
|
|
96
|
+
* console.log('Transaction completed:', result.transactionHash);
|
|
97
|
+
* // Handle successful transaction
|
|
98
|
+
* if (result.shouldRedirect && result.redirectUrl) {
|
|
99
|
+
* // Navigate to success page or external URL
|
|
100
|
+
* }
|
|
72
101
|
* }
|
|
73
102
|
* } catch (error) {
|
|
74
|
-
* console.error('
|
|
103
|
+
* console.error('Transaction failed:', error.message);
|
|
104
|
+
* // Handle error (show user-friendly message)
|
|
75
105
|
* }
|
|
76
106
|
* };
|
|
77
107
|
*
|
|
78
108
|
* return (
|
|
79
|
-
* <
|
|
80
|
-
*
|
|
81
|
-
*
|
|
109
|
+
* <View style={styles.container}>
|
|
110
|
+
* <TouchableOpacity
|
|
111
|
+
* onPress={() => handleSign(redeemJWT)}
|
|
112
|
+
* disabled={!isSignerAvailable}
|
|
113
|
+
* style={[styles.button, !isSignerAvailable && styles.disabled]}
|
|
114
|
+
* >
|
|
115
|
+
* <Text style={styles.buttonText}>
|
|
116
|
+
* {isSignerInitialized ? 'Sign Transaction' : 'Initializing...'}
|
|
117
|
+
* </Text>
|
|
118
|
+
* </TouchableOpacity>
|
|
119
|
+
* </View>
|
|
82
120
|
* );
|
|
83
121
|
* }
|
|
84
122
|
* ```
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* **Advanced Usage with Error Handling:**
|
|
126
|
+
* ```typescript
|
|
127
|
+
* function AdvancedTransactionComponent() {
|
|
128
|
+
* const [isLoading, setIsLoading] = useState(false);
|
|
129
|
+
* const [error, setError] = useState<string | null>(null);
|
|
130
|
+
* const { signAndSubmitTransactionWithJWT, isSignerAvailable } = useTransactionSigner();
|
|
131
|
+
*
|
|
132
|
+
* const handleTransaction = async (jwt: string) => {
|
|
133
|
+
* setIsLoading(true);
|
|
134
|
+
* setError(null);
|
|
135
|
+
*
|
|
136
|
+
* try {
|
|
137
|
+
* const result = await signAndSubmitTransactionWithJWT(jwt);
|
|
138
|
+
*
|
|
139
|
+
* if (result.success) {
|
|
140
|
+
* // Success handling
|
|
141
|
+
* Alert.alert(
|
|
142
|
+
* 'Success',
|
|
143
|
+
* `Transaction completed!\nHash: ${result.transactionHash}`
|
|
144
|
+
* );
|
|
145
|
+
* } else {
|
|
146
|
+
* throw new Error(result.error || 'Transaction failed');
|
|
147
|
+
* }
|
|
148
|
+
* } catch (err) {
|
|
149
|
+
* const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
150
|
+
* setError(errorMessage);
|
|
151
|
+
*
|
|
152
|
+
* // Different error handling based on error type
|
|
153
|
+
* if (errorMessage.includes('expired')) {
|
|
154
|
+
* Alert.alert('Session Expired', 'Please log in again');
|
|
155
|
+
* } else if (errorMessage.includes('network')) {
|
|
156
|
+
* Alert.alert('Network Error', 'Please check your connection');
|
|
157
|
+
* } else {
|
|
158
|
+
* Alert.alert('Transaction Failed', errorMessage);
|
|
159
|
+
* }
|
|
160
|
+
* } finally {
|
|
161
|
+
* setIsLoading(false);
|
|
162
|
+
* }
|
|
163
|
+
* };
|
|
164
|
+
*
|
|
165
|
+
* return (
|
|
166
|
+
* <View>
|
|
167
|
+
* {error && (
|
|
168
|
+
* <Text style={styles.errorText}>{error}</Text>
|
|
169
|
+
* )}
|
|
170
|
+
* <TouchableOpacity
|
|
171
|
+
* onPress={() => handleTransaction(jwtToken)}
|
|
172
|
+
* disabled={!isSignerAvailable || isLoading}
|
|
173
|
+
* >
|
|
174
|
+
* <Text>
|
|
175
|
+
* {isLoading ? 'Processing...' : 'Sign & Submit Transaction'}
|
|
176
|
+
* </Text>
|
|
177
|
+
* </TouchableOpacity>
|
|
178
|
+
* </View>
|
|
179
|
+
* );
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* **Integration with PERS SDK:**
|
|
185
|
+
* ```typescript
|
|
186
|
+
* import { usePersSDK } from '@explorins/pers-sdk-react-native';
|
|
187
|
+
* import { useTransactionSigner } from '@explorins/pers-sdk-react-native';
|
|
188
|
+
*
|
|
189
|
+
* function RedemptionFlow() {
|
|
190
|
+
* const { user, redeemTokens } = usePersSDK();
|
|
191
|
+
* const { signAndSubmitTransactionWithJWT } = useTransactionSigner();
|
|
192
|
+
*
|
|
193
|
+
* const handleRedemption = async (tokenAmount: number) => {
|
|
194
|
+
* try {
|
|
195
|
+
* // Step 1: Create redemption with PERS SDK
|
|
196
|
+
* const redemption = await redeemTokens({
|
|
197
|
+
* tokenAmount,
|
|
198
|
+
* destinationAddress: user?.walletAddress
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* // Step 2: Sign and submit transaction
|
|
202
|
+
* const txResult = await signAndSubmitTransactionWithJWT(redemption.jwtToken);
|
|
203
|
+
*
|
|
204
|
+
* if (txResult.success) {
|
|
205
|
+
* console.log('Redemption completed:', txResult.transactionHash);
|
|
206
|
+
* }
|
|
207
|
+
* } catch (error) {
|
|
208
|
+
* console.error('Redemption failed:', error);
|
|
209
|
+
* }
|
|
210
|
+
* };
|
|
211
|
+
* }
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
214
|
+
* @see {@link SubmissionResult} for transaction result structure
|
|
215
|
+
* @see {@link TransactionSigningResult} for signing result details
|
|
216
|
+
* @see {@link AuthenticatedUser} for user authentication data
|
|
217
|
+
*
|
|
218
|
+
* @since 1.5.0
|
|
85
219
|
*/
|
|
86
|
-
export const useTransactionSigner = () => {
|
|
87
|
-
const { isInitialized, isAuthenticated, user } = usePersSDK();
|
|
220
|
+
export const useTransactionSigner = (): TransactionSignerHook => {
|
|
221
|
+
// const { isInitialized, isAuthenticated, user } = usePersSDK();
|
|
88
222
|
const [isSignerInitialized, setIsSignerInitialized] = useState(false);
|
|
89
|
-
const signerSDKRef = useRef<
|
|
223
|
+
const signerSDKRef = useRef<PersSignerSDK | null>(null);
|
|
90
224
|
|
|
91
225
|
// Auto-initialize signer SDK when PERS SDK is ready
|
|
92
226
|
useEffect(() => {
|
|
93
|
-
console.log('[useTransactionSigner] Auto-initializing signer SDK...');
|
|
94
|
-
|
|
95
227
|
const initializeSignerSDK = async () => {
|
|
96
228
|
if (!createPersSignerSDK) {
|
|
97
229
|
console.warn('[useTransactionSigner] Signer SDK not available');
|
|
@@ -99,14 +231,14 @@ export const useTransactionSigner = () => {
|
|
|
99
231
|
}
|
|
100
232
|
|
|
101
233
|
try {
|
|
102
|
-
|
|
234
|
+
// React Native createPersSignerSDK automatically includes WebAuthn provider
|
|
103
235
|
const signerSDK = await createPersSignerSDK({
|
|
104
|
-
ethersProviderUrl: DEFAULT_ETHERS_PROVIDER
|
|
236
|
+
ethersProviderUrl: DEFAULT_ETHERS_PROVIDER,
|
|
237
|
+
relyingPartyName: 'PERS React Native App'
|
|
105
238
|
});
|
|
106
239
|
|
|
107
240
|
signerSDKRef.current = signerSDK;
|
|
108
241
|
setIsSignerInitialized(true);
|
|
109
|
-
console.log('[useTransactionSigner] PERS Signer SDK initialized successfully');
|
|
110
242
|
|
|
111
243
|
} catch (error) {
|
|
112
244
|
console.error('[useTransactionSigner] Failed to initialize signer SDK:', error);
|
|
@@ -119,17 +251,85 @@ export const useTransactionSigner = () => {
|
|
|
119
251
|
}, [isSignerInitialized]);
|
|
120
252
|
|
|
121
253
|
/**
|
|
122
|
-
* Sign a blockchain transaction using JWT token
|
|
254
|
+
* Sign and submit a blockchain transaction using JWT token
|
|
255
|
+
*
|
|
256
|
+
* This is the main method for executing blockchain transactions in React Native.
|
|
257
|
+
* It handles the complete transaction lifecycle: authentication, signing, and
|
|
258
|
+
* blockchain submission in a single call.
|
|
259
|
+
*
|
|
260
|
+
* **Process Flow:**
|
|
261
|
+
* 1. Validates the JWT token and extracts transaction data
|
|
262
|
+
* 2. Authenticates the user using WebAuthn (cached for 5 minutes)
|
|
263
|
+
* 3. Fetches transaction data from PERS backend
|
|
264
|
+
* 4. Signs the transaction using device security (biometrics/PIN)
|
|
265
|
+
* 5. Submits the signed transaction to the blockchain
|
|
266
|
+
* 6. Returns detailed result with transaction hash and status
|
|
267
|
+
*
|
|
268
|
+
* **Security Features:**
|
|
269
|
+
* - JWT token validation and expiration checking
|
|
270
|
+
* - WebAuthn authentication with device biometrics
|
|
271
|
+
* - Secure transaction signing without exposing private keys
|
|
272
|
+
* - Automatic session management with secure caching
|
|
273
|
+
*
|
|
274
|
+
* @param {string} jwt - JWT token containing transaction ID and user information
|
|
275
|
+
* Must include: `transactionId`, `identifierEmail`, `tenantId`
|
|
276
|
+
* @returns {Promise<SubmissionResult>} Complete transaction result
|
|
277
|
+
*
|
|
278
|
+
* @throws {Error} 'Transaction signer not initialized' - Hook not ready
|
|
279
|
+
* @throws {Error} 'PERS Signer SDK not available' - SDK installation issue
|
|
280
|
+
* @throws {Error} 'Invalid or expired JWT token' - Token validation failed
|
|
281
|
+
* @throws {Error} 'Authentication failed' - WebAuthn authentication failed
|
|
282
|
+
* @throws {Error} 'Transaction signing failed' - Signing process failed
|
|
283
|
+
* @throws {Error} 'Transaction submission failed' - Blockchain submission failed
|
|
123
284
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
285
|
+
* @example
|
|
286
|
+
* **Basic Transaction:**
|
|
287
|
+
* ```typescript
|
|
288
|
+
* const { signAndSubmitTransactionWithJWT } = useTransactionSigner();
|
|
128
289
|
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
290
|
+
* const result = await signAndSubmitTransactionWithJWT(jwtToken);
|
|
291
|
+
* if (result.success) {
|
|
292
|
+
* console.log('Transaction Hash:', result.transactionHash);
|
|
293
|
+
* console.log('Blockchain explorer: [Chain-specific explorer URL]');
|
|
294
|
+
* }
|
|
295
|
+
* ```
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* **With Error Handling:**
|
|
299
|
+
* ```typescript
|
|
300
|
+
* try {
|
|
301
|
+
* const result = await signAndSubmitTransactionWithJWT(redeemJWT);
|
|
302
|
+
*
|
|
303
|
+
* if (result.success) {
|
|
304
|
+
* // Transaction successful
|
|
305
|
+
* setTransactionHash(result.transactionHash);
|
|
306
|
+
*
|
|
307
|
+
* if (result.shouldRedirect && result.redirectUrl) {
|
|
308
|
+
* // Handle post-transaction redirect
|
|
309
|
+
* Linking.openURL(result.redirectUrl);
|
|
310
|
+
* }
|
|
311
|
+
* } else {
|
|
312
|
+
* // Transaction failed but didn't throw
|
|
313
|
+
* setError(result.error || 'Transaction failed');
|
|
314
|
+
* }
|
|
315
|
+
* } catch (error) {
|
|
316
|
+
* // Handle different error types
|
|
317
|
+
* if (error.message.includes('expired')) {
|
|
318
|
+
* // Token expired - redirect to login
|
|
319
|
+
* navigation.navigate('Login');
|
|
320
|
+
* } else if (error.message.includes('network')) {
|
|
321
|
+
* // Network error - suggest retry
|
|
322
|
+
* setError('Network error. Please try again.');
|
|
323
|
+
* } else {
|
|
324
|
+
* // Other errors
|
|
325
|
+
* setError(error.message);
|
|
326
|
+
* }
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*
|
|
330
|
+
* @see {@link SubmissionResult} for detailed result structure
|
|
131
331
|
*/
|
|
132
|
-
const
|
|
332
|
+
const signAndSubmitTransactionWithJWT = useCallback(async (jwt: string): Promise<SubmissionResult> => {
|
|
133
333
|
if (!isSignerInitialized || !signerSDKRef.current) {
|
|
134
334
|
throw new Error('Transaction signer not initialized');
|
|
135
335
|
}
|
|
@@ -138,128 +338,84 @@ export const useTransactionSigner = () => {
|
|
|
138
338
|
}
|
|
139
339
|
|
|
140
340
|
try {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Create URLSearchParams from JWT token (matching web frontend)
|
|
144
|
-
const searchParams = new URLSearchParams();
|
|
145
|
-
searchParams.set('jwt', jwt);
|
|
341
|
+
// Use the actual SDK method that handles the complete sign + submit flow
|
|
342
|
+
const submissionResult = await signerSDKRef.current.signAndSubmitPersTransaction(jwt);
|
|
146
343
|
|
|
147
|
-
|
|
148
|
-
console.log('[useTransactionSigner] Authenticating with JWT...');
|
|
149
|
-
const authResult = await signerSDKRef.current.authenticateWithJWT(searchParams);
|
|
150
|
-
|
|
151
|
-
if (!authResult) {
|
|
152
|
-
throw new Error('JWT authentication failed - no valid token found');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (authResult.isExpired) {
|
|
156
|
-
throw new Error('JWT token is expired');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
console.log('[useTransactionSigner] JWT authentication successful:', authResult.user.identifier);
|
|
160
|
-
|
|
161
|
-
// Step 2: Extract transaction ID from JWT
|
|
162
|
-
const jwtParts = jwt.split('.');
|
|
163
|
-
if (jwtParts.length !== 3) {
|
|
164
|
-
throw new Error('Invalid JWT token format');
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const payload = JSON.parse(atob(jwtParts[1]));
|
|
168
|
-
const transactionId = payload.transactionId;
|
|
169
|
-
|
|
170
|
-
if (!transactionId) {
|
|
171
|
-
throw new Error('No transaction ID found in JWT token');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
console.log('[useTransactionSigner] Found transaction ID:', transactionId);
|
|
175
|
-
|
|
176
|
-
// Step 3: Sign transaction using authenticated user
|
|
177
|
-
console.log('[useTransactionSigner] Signing transaction...');
|
|
178
|
-
const signingResult = await signerSDKRef.current.signPersTransaction(
|
|
179
|
-
authResult.user,
|
|
180
|
-
transactionId
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
console.log('[useTransactionSigner] Transaction signing result:', signingResult);
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
success: signingResult.success,
|
|
187
|
-
transactionHash: signingResult.transactionHash,
|
|
188
|
-
error: signingResult.error
|
|
189
|
-
};
|
|
344
|
+
return submissionResult;
|
|
190
345
|
|
|
191
346
|
} catch (error) {
|
|
192
347
|
console.error('[useTransactionSigner] JWT transaction signing failed:', error);
|
|
193
|
-
|
|
194
|
-
success: false,
|
|
195
|
-
error: `Transaction signing failed: ${error}`
|
|
196
|
-
};
|
|
348
|
+
throw error; // Re-throw to maintain error handling upstream
|
|
197
349
|
}
|
|
198
350
|
}, [isSignerInitialized]);
|
|
199
351
|
|
|
200
|
-
/**
|
|
201
|
-
* Manual user authentication (fallback method)
|
|
202
|
-
*/
|
|
203
|
-
const authenticateUser = useCallback(async (identifier: string): Promise<SignerAuthenticatedUser | null> => {
|
|
204
|
-
if (!isSignerInitialized || !signerSDKRef.current) {
|
|
205
|
-
throw new Error('Transaction signer not initialized');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
console.log('[useTransactionSigner] Manual user authentication:', identifier);
|
|
210
|
-
const user = await signerSDKRef.current.authenticateUser(identifier);
|
|
211
|
-
console.log('[useTransactionSigner] Manual authentication successful');
|
|
212
|
-
return user;
|
|
213
|
-
} catch (error) {
|
|
214
|
-
console.error('[useTransactionSigner] Manual authentication failed:', error);
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
}, [isSignerInitialized]);
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Legacy method for backward compatibility
|
|
221
|
-
* @deprecated Use signTransactionWithJWT instead
|
|
222
|
-
*/
|
|
223
|
-
const signTransaction = useCallback(async (jwt: string): Promise<TransactionSigningResult> => {
|
|
224
|
-
console.warn('[useTransactionSigner] signTransaction is deprecated, use signTransactionWithJWT');
|
|
225
|
-
return signTransactionWithJWT(jwt);
|
|
226
|
-
}, [signTransactionWithJWT]);
|
|
227
|
-
|
|
228
352
|
return {
|
|
229
353
|
/**
|
|
230
|
-
* Sign a blockchain transaction using JWT token
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*
|
|
236
|
-
* @
|
|
237
|
-
*/
|
|
238
|
-
signTransaction,
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Manual user authentication (for advanced use cases)
|
|
354
|
+
* Sign and submit a blockchain transaction using JWT token
|
|
355
|
+
*
|
|
356
|
+
* Main method for executing blockchain transactions. Handles complete flow
|
|
357
|
+
* from authentication to blockchain submission in a single call.
|
|
358
|
+
*
|
|
359
|
+
* @param {string} jwt - JWT token with transaction and user data
|
|
360
|
+
* @returns {Promise<SubmissionResult>} Transaction result with hash and status
|
|
242
361
|
*/
|
|
243
|
-
|
|
362
|
+
signAndSubmitTransactionWithJWT,
|
|
244
363
|
|
|
245
364
|
/**
|
|
246
|
-
* Whether the transaction signer has been initialized
|
|
365
|
+
* Whether the transaction signer SDK has been successfully initialized
|
|
366
|
+
*
|
|
367
|
+
* Use this to show loading states while the signer is being set up.
|
|
368
|
+
* When `false`, the signer is still initializing and transactions cannot be processed.
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```typescript
|
|
372
|
+
* const { isSignerInitialized } = useTransactionSigner();
|
|
373
|
+
*
|
|
374
|
+
* if (!isSignerInitialized) {
|
|
375
|
+
* return <LoadingSpinner text="Initializing signer..." />;
|
|
376
|
+
* }
|
|
377
|
+
* ```
|
|
247
378
|
*/
|
|
248
379
|
isSignerInitialized,
|
|
249
380
|
|
|
250
381
|
/**
|
|
251
|
-
* Whether transaction signing is available
|
|
382
|
+
* Whether blockchain transaction signing is fully available
|
|
383
|
+
*
|
|
384
|
+
* This combines multiple checks: SDK availability, initialization status,
|
|
385
|
+
* and configuration validity. Use this to enable/disable transaction buttons.
|
|
386
|
+
*
|
|
387
|
+
* Returns `true` only when:
|
|
388
|
+
* - PERS Signer SDK is installed and available
|
|
389
|
+
* - Signer has been successfully initialized
|
|
390
|
+
* - All required dependencies are loaded
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* const { isSignerAvailable } = useTransactionSigner();
|
|
395
|
+
*
|
|
396
|
+
* return (
|
|
397
|
+
* <TouchableOpacity
|
|
398
|
+
* disabled={!isSignerAvailable}
|
|
399
|
+
* style={[styles.button, !isSignerAvailable && styles.disabled]}
|
|
400
|
+
* >
|
|
401
|
+
* <Text>{isSignerAvailable ? 'Sign Transaction' : 'Signer Unavailable'}</Text>
|
|
402
|
+
* </TouchableOpacity>
|
|
403
|
+
* );
|
|
404
|
+
* ```
|
|
252
405
|
*/
|
|
253
406
|
isSignerAvailable: isSignerInitialized && !!createPersSignerSDK,
|
|
254
407
|
};
|
|
255
408
|
};
|
|
256
409
|
|
|
257
410
|
/**
|
|
258
|
-
* Type
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
*
|
|
411
|
+
* Type exports for external usage
|
|
412
|
+
*
|
|
413
|
+
* These types are re-exported from the PERS Signer SDK for convenience
|
|
414
|
+
* when working with transaction signing in React Native applications.
|
|
415
|
+
*
|
|
416
|
+
* @see {@link SubmissionResult} - Complete transaction submission result
|
|
417
|
+
* @see {@link AuthenticatedUser} - User authentication data structure
|
|
418
|
+
* @see {@link TransactionSigningResult} - Transaction signing result details
|
|
419
|
+
* @see {@link TransactionSignerHook} - Hook return interface
|
|
264
420
|
*/
|
|
265
|
-
export type {
|
|
421
|
+
export type { SubmissionResult, AuthenticatedUser, TransactionSigningResult, TransactionSignerHook };
|