@explorins/pers-sdk-react-native 1.5.35 → 2.0.0

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.
Files changed (55) hide show
  1. package/README.md +402 -13
  2. package/dist/hooks/index.d.ts +3 -1
  3. package/dist/hooks/index.d.ts.map +1 -1
  4. package/dist/hooks/index.js +1 -0
  5. package/dist/hooks/useBusiness.d.ts +5 -5
  6. package/dist/hooks/useBusiness.d.ts.map +1 -1
  7. package/dist/hooks/useCampaigns.d.ts +8 -8
  8. package/dist/hooks/useCampaigns.d.ts.map +1 -1
  9. package/dist/hooks/useCampaigns.js +3 -3
  10. package/dist/hooks/useDonations.d.ts +2 -2
  11. package/dist/hooks/useDonations.d.ts.map +1 -1
  12. package/dist/hooks/useEvents.d.ts +178 -0
  13. package/dist/hooks/useEvents.d.ts.map +1 -0
  14. package/dist/hooks/useEvents.js +312 -0
  15. package/dist/hooks/usePurchases.d.ts +3 -3
  16. package/dist/hooks/usePurchases.d.ts.map +1 -1
  17. package/dist/hooks/useRedemptions.d.ts +6 -5
  18. package/dist/hooks/useRedemptions.d.ts.map +1 -1
  19. package/dist/hooks/useRedemptions.js +13 -19
  20. package/dist/hooks/useTenants.d.ts +2 -2
  21. package/dist/hooks/useTenants.d.ts.map +1 -1
  22. package/dist/hooks/useTokens.d.ts +4 -4
  23. package/dist/hooks/useTokens.d.ts.map +1 -1
  24. package/dist/hooks/useTransactionSigner.d.ts +2 -0
  25. package/dist/hooks/useTransactionSigner.d.ts.map +1 -1
  26. package/dist/hooks/useTransactionSigner.js +68 -0
  27. package/dist/hooks/useTransactions.d.ts +3 -3
  28. package/dist/hooks/useTransactions.d.ts.map +1 -1
  29. package/dist/hooks/useUserStatus.d.ts +3 -3
  30. package/dist/hooks/useUserStatus.d.ts.map +1 -1
  31. package/dist/hooks/useUsers.d.ts +3 -3
  32. package/dist/hooks/useUsers.d.ts.map +1 -1
  33. package/dist/hooks/useWeb3.d.ts +5 -53
  34. package/dist/hooks/useWeb3.d.ts.map +1 -1
  35. package/dist/hooks/useWeb3.js +82 -177
  36. package/dist/index.d.ts +83 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +3227 -837
  39. package/dist/index.js.map +1 -1
  40. package/package.json +3 -3
  41. package/src/hooks/index.ts +3 -1
  42. package/src/hooks/useBusiness.ts +5 -5
  43. package/src/hooks/useCampaigns.ts +12 -11
  44. package/src/hooks/useDonations.ts +2 -2
  45. package/src/hooks/useEvents.ts +360 -0
  46. package/src/hooks/usePurchases.ts +3 -3
  47. package/src/hooks/useRedemptions.ts +16 -22
  48. package/src/hooks/useTenants.ts +2 -2
  49. package/src/hooks/useTokens.ts +4 -4
  50. package/src/hooks/useTransactionSigner.ts +73 -0
  51. package/src/hooks/useTransactions.ts +4 -3
  52. package/src/hooks/useUserStatus.ts +3 -3
  53. package/src/hooks/useUsers.ts +3 -3
  54. package/src/hooks/useWeb3.ts +98 -202
  55. package/src/index.ts +105 -2
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { TokenDTO } from '@explorins/pers-shared';
3
+ import type { TokenDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
4
4
 
5
5
  /**
6
6
  * React hook for token operations in the PERS SDK
@@ -50,7 +50,7 @@ export const useTokens = () => {
50
50
  * console.log('Available tokens:', tokens);
51
51
  * ```
52
52
  */
53
- const getTokens = useCallback(async (): Promise<TokenDTO[]> => {
53
+ const getTokens = useCallback(async (): Promise<PaginatedResponseDTO<TokenDTO>> => {
54
54
  if (!isInitialized || !sdk) {
55
55
  throw new Error('SDK not initialized. Call initialize() first.');
56
56
  }
@@ -104,7 +104,7 @@ export const useTokens = () => {
104
104
  * console.log('Reward tokens:', rewardTokens);
105
105
  * ```
106
106
  */
107
- const getRewardTokens = useCallback(async (): Promise<TokenDTO[]> => {
107
+ const getRewardTokens = useCallback(async (): Promise<PaginatedResponseDTO<TokenDTO>> => {
108
108
  if (!isInitialized || !sdk) {
109
109
  throw new Error('SDK not initialized. Call initialize() first.');
110
110
  }
@@ -158,7 +158,7 @@ export const useTokens = () => {
158
158
  * console.log('Status tokens:', statusTokens);
159
159
  * ```
160
160
  */
161
- const getStatusTokens = useCallback(async (): Promise<TokenDTO[]> => {
161
+ const getStatusTokens = useCallback(async (): Promise<PaginatedResponseDTO<TokenDTO>> => {
162
162
  if (!isInitialized || !sdk) {
163
163
  throw new Error('SDK not initialized. Call initialize() first.');
164
164
  }
@@ -38,6 +38,7 @@ try {
38
38
  *
39
39
  * @interface TransactionSignerHook
40
40
  * @property {Function} signAndSubmitTransactionWithJWT - Main method to sign and submit transactions
41
+ * @property {Function} signPersTransaction - Sign transaction without submitting (for POS flows)
41
42
  * @property {boolean} isSignerInitialized - Whether the signer SDK has been initialized
42
43
  * @property {boolean} isSignerAvailable - Whether signing functionality is fully available
43
44
  * @property {SigningStatus | null} currentStatus - Current signing status for UI feedback
@@ -45,6 +46,7 @@ try {
45
46
  */
46
47
  export interface TransactionSignerHook {
47
48
  signAndSubmitTransactionWithJWT: (jwt: string, onStatusUpdate?: OnStatusUpdateFn) => Promise<SubmissionResult>;
49
+ signPersTransaction: (jwt: string, onStatusUpdate?: OnStatusUpdateFn) => Promise<TransactionSigningResult>;
48
50
  isSignerInitialized: boolean;
49
51
  isSignerAvailable: boolean;
50
52
  currentStatus: SigningStatus | null;
@@ -386,6 +388,65 @@ export const useTransactionSigner = (): TransactionSignerHook => {
386
388
  }
387
389
  }, [isSignerInitialized]);
388
390
 
391
+ /**
392
+ * Sign a PERS transaction without submitting (for POS flows with QR codes)
393
+ *
394
+ * This method signs the transaction but does NOT submit it to the blockchain.
395
+ * Instead, it returns the signature which can be displayed as a QR code for
396
+ * the business to scan and submit.
397
+ *
398
+ * **Use Case:** Point-of-Sale (POS) flows where:
399
+ * 1. User signs the transaction
400
+ * 2. App generates QR code with signature
401
+ * 3. Business scans QR code
402
+ * 4. Business submits transaction to blockchain
403
+ *
404
+ * @param {string} jwt - JWT token with transaction and user data
405
+ * @param {StatusCallback} [onStatusUpdate] - Optional callback for status updates
406
+ * @returns {Promise<TransactionSigningResult>} Signing result with signature for QR code
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * const { signPersTransaction } = useTransactionSigner();
411
+ *
412
+ * const result = await signPersTransaction(jwtToken);
413
+ * if (result.success) {
414
+ * const qrData = {
415
+ * transactionId: result.transactionId,
416
+ * signature: result.signature,
417
+ * transactionFormat: result.signingData.format,
418
+ * txType: 'PENDING_SUBMISSION'
419
+ * };
420
+ * // Display QR code with qrData
421
+ * }
422
+ * ```
423
+ */
424
+ const signPersTransaction = useCallback(async (jwt: string, onStatusUpdate?: OnStatusUpdateFn): Promise<TransactionSigningResult> => {
425
+ if (!isSignerInitialized || !signerSDKRef.current) {
426
+ throw new Error('Signer SDK not initialized');
427
+ }
428
+
429
+ // Create status callback wrapper
430
+ const statusCallback: StatusCallback = {
431
+ onStatusUpdate: (status: SigningStatus, message: string, data?: StatusUpdateData) => {
432
+ setCurrentStatus(status);
433
+ setStatusMessage(message);
434
+ onStatusUpdate?.(status, message, data);
435
+ }
436
+ };
437
+
438
+ try {
439
+ // Use the SDK's sign-only method
440
+ const signingResult = await signerSDKRef.current.signPersTransaction(jwt, statusCallback);
441
+ return signingResult;
442
+ } catch (error) {
443
+ console.error('[useTransactionSigner] Sign-only transaction failed:', error);
444
+ setCurrentStatus(SigningStatus.ERROR);
445
+ setStatusMessage(error instanceof Error ? error.message : 'Signing failed');
446
+ throw error;
447
+ }
448
+ }, [isSignerInitialized]);
449
+
389
450
  return {
390
451
  /**
391
452
  * Sign and submit a blockchain transaction using JWT token
@@ -398,6 +459,18 @@ export const useTransactionSigner = (): TransactionSignerHook => {
398
459
  * @returns {Promise<SubmissionResult>} Transaction result with hash and status
399
460
  */
400
461
  signAndSubmitTransactionWithJWT,
462
+
463
+ /**
464
+ * Sign a transaction without submitting (for POS flows)
465
+ *
466
+ * Signs the transaction and returns the signature without submitting to blockchain.
467
+ * Use this for Point-of-Sale flows where the business scans a QR code and submits.
468
+ *
469
+ * @param {string} jwt - JWT token with transaction and user data
470
+ * @param {StatusCallback} [onStatusUpdate] - Optional callback for status updates
471
+ * @returns {Promise<TransactionSigningResult>} Signing result with signature for QR code
472
+ */
473
+ signPersTransaction,
401
474
 
402
475
  /**
403
476
  * Whether the transaction signer SDK has been successfully initialized
@@ -6,7 +6,8 @@ import type {
6
6
  TransactionRequestResponseDTO,
7
7
  TransactionDTO,
8
8
  TransactionRole,
9
- TransactionPaginationRequestDTO
9
+ TransactionPaginationRequestDTO,
10
+ PaginatedResponseDTO
10
11
  } from '@explorins/pers-shared';
11
12
 
12
13
  /**
@@ -164,7 +165,7 @@ export const useTransactions = () => {
164
165
  * const allTransactions = await getUserTransactionHistory(); // No filter
165
166
  * ```
166
167
  */
167
- const getUserTransactionHistory = useCallback(async (role?: TransactionRole): Promise<TransactionDTO[]> => {
168
+ const getUserTransactionHistory = useCallback(async (role?: TransactionRole): Promise<PaginatedResponseDTO<TransactionDTO>> => {
168
169
 
169
170
  if (!isInitialized || !sdk) {
170
171
  throw new Error('SDK not initialized. Call initialize() first.');
@@ -179,7 +180,7 @@ export const useTransactions = () => {
179
180
  }
180
181
  }, [sdk, isInitialized]);
181
182
 
182
- const getTenantTransactions = useCallback(async (): Promise<TransactionDTO[]> => {
183
+ const getTenantTransactions = useCallback(async (): Promise<PaginatedResponseDTO<TransactionDTO>> => {
183
184
  if (!isInitialized || !sdk) {
184
185
  throw new Error('SDK not initialized. Call initialize() first.');
185
186
  }
@@ -1,11 +1,11 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { UserStatusTypeDTO } from '@explorins/pers-shared';
3
+ import type { UserStatusTypeDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
4
4
 
5
5
  export const useUserStatus = () => {
6
6
  const { sdk, isInitialized, isAuthenticated } = usePersSDK();
7
7
 
8
- const getUserStatusTypes = useCallback(async (): Promise<UserStatusTypeDTO[]> => {
8
+ const getUserStatusTypes = useCallback(async (): Promise<PaginatedResponseDTO<UserStatusTypeDTO>> => {
9
9
  if (!isInitialized || !sdk) {
10
10
  throw new Error('SDK not initialized. Call initialize() first.');
11
11
  }
@@ -19,7 +19,7 @@ export const useUserStatus = () => {
19
19
  }
20
20
  }, [sdk, isInitialized]);
21
21
 
22
- const getEarnedUserStatus = useCallback(async (): Promise<UserStatusTypeDTO[]> => {
22
+ const getEarnedUserStatus = useCallback(async (): Promise<PaginatedResponseDTO<UserStatusTypeDTO>> => {
23
23
  if (!isInitialized || !sdk) {
24
24
  throw new Error('SDK not initialized. Call initialize() first.');
25
25
  }
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
- import type { UserDTO, UserCreateRequestDTO } from '@explorins/pers-shared';
3
+ import type { UserDTO, UserCreateRequestDTO, PaginatedResponseDTO } from '@explorins/pers-shared';
4
4
  import type { UserPublicProfileDTO } from '@explorins/pers-sdk/user';
5
5
 
6
6
  export const useUsers = () => {
@@ -54,7 +54,7 @@ export const useUsers = () => {
54
54
  }
55
55
  }, [sdk, isInitialized]);
56
56
 
57
- const getAllUsersPublic = useCallback(async (filter?: { key: string; value: string }): Promise<UserPublicProfileDTO[]> => {
57
+ const getAllUsersPublic = useCallback(async (filter?: { key: string; value: string }): Promise<PaginatedResponseDTO<UserPublicProfileDTO>> => {
58
58
  if (!isInitialized || !sdk) {
59
59
  throw new Error('SDK not initialized. Call initialize() first.');
60
60
  }
@@ -69,7 +69,7 @@ export const useUsers = () => {
69
69
  }, [sdk, isInitialized]);
70
70
 
71
71
  // Admin methods
72
- const getAllUsers = useCallback(async (): Promise<UserDTO[]> => {
72
+ const getAllUsers = useCallback(async (): Promise<PaginatedResponseDTO<UserDTO>> => {
73
73
  if (!isInitialized || !sdk) {
74
74
  throw new Error('SDK not initialized. Call initialize() first.');
75
75
  }
@@ -9,19 +9,10 @@ import type {
9
9
  } from '@explorins/pers-sdk/web3';
10
10
  import type { ChainData } from '@explorins/pers-sdk/web3-chain';
11
11
  import type { TokenDTO } from '@explorins/pers-shared';
12
- import { NativeTokenTypes } from '@explorins/pers-shared';
12
+ import type { AccountOwnedTokensResult } from '@explorins/pers-sdk';
13
13
 
14
- /**
15
- * Result of getting user's owned tokens from a contract
16
- */
17
- export interface UserOwnedTokensResult {
18
- /** The token definition (contains contract info, ABI, metadata definitions) */
19
- token: TokenDTO;
20
- /** Array of token balances the user owns (filtered to hasBalance: true) */
21
- ownedTokens: readonly TokenBalance[];
22
- /** Total count of owned tokens */
23
- totalOwned: number;
24
- }
14
+ // Re-export for convenience
15
+ export type { AccountOwnedTokensResult } from '@explorins/pers-sdk';
25
16
 
26
17
  /**
27
18
  * React hook for Web3 operations in the PERS SDK
@@ -32,48 +23,9 @@ export interface UserOwnedTokensResult {
32
23
  *
33
24
  * Note: Wallet addresses should be obtained from `user.wallets` via `usePersSDK()`.
34
25
  *
35
- * ## ERC-1155 Token Handling
36
- *
37
- * **IMPORTANT**: ERC-1155 tokens require specific `tokenIds` to query balances.
38
- * Unlike ERC-721, you cannot enumerate owned tokens - you must know which tokenId to check.
39
- *
40
- * The tokenIds come from `TokenDTO.metadata[].tokenMetadataIncrementalId`.
41
- *
42
- * ### Option 1: Use the helper method (Recommended)
43
- * ```typescript
44
- * const { getUserOwnedTokensFromContract } = useWeb3();
45
- * const { getRewardTokens } = useTokens();
46
- *
47
- * const rewardTokens = await getRewardTokens();
48
- * const result = await getUserOwnedTokensFromContract(walletAddress, rewardTokens[0]);
49
- * console.log('Owned rewards:', result.ownedTokens);
50
- * ```
51
- *
52
- * ### Option 2: Manual tokenIds extraction
53
- * ```typescript
54
- * const { getTokenCollection, extractTokenIds } = useWeb3();
55
- * const { getRewardTokens } = useTokens();
56
- *
57
- * const rewardTokens = await getRewardTokens();
58
- * const rewardToken = rewardTokens[0];
59
- *
60
- * // Extract tokenIds for ERC-1155
61
- * const tokenIds = extractTokenIds(rewardToken);
62
- *
63
- * const collection = await getTokenCollection({
64
- * accountAddress: walletAddress,
65
- * contractAddress: rewardToken.contractAddress,
66
- * abi: rewardToken.abi,
67
- * chainId: rewardToken.chainId,
68
- * tokenIds: tokenIds // Required for ERC-1155!
69
- * });
70
- *
71
- * const ownedTokens = collection.tokens.filter(t => t.hasBalance);
72
- * ```
73
- *
74
26
  * @returns Web3 hook with methods for blockchain operations
75
27
  *
76
- * @example Basic Usage
28
+ * @example
77
29
  * ```typescript
78
30
  * function Web3Component() {
79
31
  * const { user } = usePersSDK();
@@ -199,152 +151,6 @@ export const useWeb3 = () => {
199
151
  }
200
152
  }, [sdk, isInitialized]);
201
153
 
202
- /**
203
- * Extract tokenIds from a TokenDTO for use with ERC-1155 contracts.
204
- *
205
- * ERC-1155 tokens require specific tokenIds to query balances. This helper
206
- * extracts the `tokenMetadataIncrementalId` from each metadata entry.
207
- *
208
- * @param token - Token definition containing metadata array
209
- * @returns Array of tokenId strings, or undefined if no metadata
210
- *
211
- * @example
212
- * ```typescript
213
- * const { extractTokenIds, getTokenCollection } = useWeb3();
214
- * const { getRewardTokens } = useTokens();
215
- *
216
- * const rewardToken = (await getRewardTokens())[0];
217
- * const tokenIds = extractTokenIds(rewardToken);
218
- *
219
- * if (tokenIds) {
220
- * const collection = await getTokenCollection({
221
- * accountAddress: walletAddress,
222
- * contractAddress: rewardToken.contractAddress,
223
- * abi: rewardToken.abi,
224
- * chainId: rewardToken.chainId,
225
- * tokenIds: tokenIds
226
- * });
227
- * }
228
- * ```
229
- */
230
- const extractTokenIds = useCallback((token: TokenDTO): string[] | undefined => {
231
- if (!token.metadata || token.metadata.length === 0) {
232
- return undefined;
233
- }
234
- return token.metadata.map(meta => meta.tokenMetadataIncrementalId.toString());
235
- }, []);
236
-
237
- /**
238
- * Get user's owned tokens from a specific token contract.
239
- *
240
- * This is a convenience method that automatically handles:
241
- * - ERC-1155 tokenId extraction from metadata
242
- * - Building the collection request
243
- * - Filtering to only tokens with balance > 0
244
- *
245
- * **This is the recommended way to get user's owned reward/status tokens.**
246
- *
247
- * @param walletAddress - User's wallet address
248
- * @param token - Token definition (from getRewardTokens, getStatusTokens, etc.)
249
- * @param maxTokens - Maximum tokens to retrieve (default: 50)
250
- * @returns Promise resolving to result with owned tokens
251
- * @throws Error if SDK is not initialized
252
- *
253
- * @example Get User's Owned Rewards
254
- * ```typescript
255
- * const { user } = usePersSDK();
256
- * const { getRewardTokens } = useTokens();
257
- * const { getUserOwnedTokensFromContract } = useWeb3();
258
- *
259
- * const walletAddress = user?.wallets?.[0]?.address;
260
- * const rewardTokens = await getRewardTokens();
261
- *
262
- * for (const token of rewardTokens) {
263
- * const result = await getUserOwnedTokensFromContract(walletAddress, token);
264
- *
265
- * console.log(`${token.name}: ${result.totalOwned} owned`);
266
- * result.ownedTokens.forEach(owned => {
267
- * console.log(` - TokenId ${owned.tokenId}: ${owned.balance}`);
268
- * console.log(` Name: ${owned.metadata?.name}`);
269
- * console.log(` Image: ${owned.metadata?.imageUrl}`);
270
- * });
271
- * }
272
- * ```
273
- */
274
- const getUserOwnedTokensFromContract = useCallback(async (
275
- walletAddress: string,
276
- token: TokenDTO,
277
- maxTokens: number = 50
278
- ): Promise<UserOwnedTokensResult> => {
279
- if (!isInitialized || !sdk) {
280
- throw new Error('SDK not initialized. Call initialize() first.');
281
- }
282
-
283
- try {
284
- // For ERC-1155, extract tokenIds from metadata
285
- const tokenIds = token.type === NativeTokenTypes.ERC1155 ? extractTokenIds(token) : undefined;
286
-
287
- const collection = await sdk.web3.getTokenCollection({
288
- accountAddress: walletAddress,
289
- contractAddress: token.contractAddress,
290
- abi: token.abi,
291
- chainId: token.chainId,
292
- tokenIds,
293
- maxTokens
294
- });
295
-
296
- // Filter to only owned tokens (hasBalance: true)
297
- // For ERC721, include all since they're enumerated
298
- const ownedTokens = token.type === NativeTokenTypes.ERC721
299
- ? collection.tokens
300
- : collection.tokens.filter(t => t.hasBalance);
301
-
302
- return {
303
- token,
304
- ownedTokens,
305
- totalOwned: ownedTokens.length
306
- };
307
- } catch (error) {
308
- console.error('Failed to get user owned tokens:', error);
309
- throw error;
310
- }
311
- }, [sdk, isInitialized, extractTokenIds]);
312
-
313
- /**
314
- * Build a TokenCollectionRequest from a TokenDTO.
315
- *
316
- * Automatically handles ERC-1155 tokenId extraction from metadata.
317
- * Use this when you need more control over the request than
318
- * `getUserOwnedTokensFromContract` provides.
319
- *
320
- * @param walletAddress - User's wallet address
321
- * @param token - Token definition
322
- * @param maxTokens - Maximum tokens to retrieve (default: 50)
323
- * @returns TokenCollectionRequest ready for getTokenCollection()
324
- *
325
- * @example
326
- * ```typescript
327
- * const { buildCollectionRequest, getTokenCollection } = useWeb3();
328
- *
329
- * const request = buildCollectionRequest(walletAddress, rewardToken);
330
- * const collection = await getTokenCollection(request);
331
- * ```
332
- */
333
- const buildCollectionRequest = useCallback((
334
- walletAddress: string,
335
- token: TokenDTO,
336
- maxTokens: number = 50
337
- ): TokenCollectionRequest => {
338
- return {
339
- accountAddress: walletAddress,
340
- contractAddress: token.contractAddress,
341
- abi: token.abi,
342
- chainId: token.chainId,
343
- tokenIds: token.type === NativeTokenTypes.ERC1155 ? extractTokenIds(token) : undefined,
344
- maxTokens
345
- };
346
- }, [extractTokenIds]);
347
-
348
154
  const resolveIPFSUrl = useCallback(async (url: string, chainId: number): Promise<string> => {
349
155
  if (!isInitialized || !sdk) {
350
156
  throw new Error('SDK not initialized. Call initialize() first.');
@@ -401,8 +207,99 @@ export const useWeb3 = () => {
401
207
  }
402
208
  }, [sdk, isInitialized]);
403
209
 
210
+ // ==========================================
211
+ // HELPER METHODS (delegating to core SDK)
212
+ // ==========================================
213
+
214
+ /**
215
+ * Extract tokenIds from a TokenDTO's metadata.
216
+ *
217
+ * Extracts `tokenMetadataIncrementalId` from each metadata entry. This is
218
+ * particularly useful for ERC-1155 tokens which require specific tokenIds
219
+ * to query balances, but works with any token that has metadata.
220
+ *
221
+ * **Note:** For most use cases, prefer `getAccountOwnedTokensFromContract()`
222
+ * which handles tokenId extraction automatically.
223
+ *
224
+ * @param token - Token definition containing metadata array
225
+ * @returns Array of tokenId strings, or undefined if no metadata
226
+ *
227
+ * @see {@link getAccountOwnedTokensFromContract} - Recommended helper that handles this automatically
228
+ */
229
+ const extractTokenIds = useCallback((token: TokenDTO): string[] | undefined => {
230
+ // Pure function - delegates to core SDK (no initialization required)
231
+ return sdk?.web3.extractTokenIds(token);
232
+ }, [sdk]);
233
+
234
+ /**
235
+ * Get owned tokens from a specific token contract for any blockchain address.
236
+ *
237
+ * **Recommended method** for querying token balances. Automatically handles:
238
+ * - Token type detection (ERC-20, ERC-721, ERC-1155)
239
+ * - TokenId extraction from metadata (uses `extractTokenIds()` internally for ERC-1155)
240
+ * - Building the collection request
241
+ * - Filtering to only tokens with balance > 0
242
+ *
243
+ * Works with any valid blockchain address - can query user wallets, external
244
+ * wallets, contract addresses, or any other address holding tokens.
245
+ *
246
+ * @param accountAddress - Any valid blockchain address (wallet, contract, etc.)
247
+ * @param token - Token definition (from getRewardTokens, getStatusTokens, etc.)
248
+ * @param maxTokens - Maximum tokens to retrieve (default: 50)
249
+ * @returns Promise resolving to result with owned tokens
250
+ * @throws Error if SDK is not initialized
251
+ *
252
+ * @example Query user's wallet
253
+ * ```typescript
254
+ * const { user } = usePersSDK();
255
+ * const { getAccountOwnedTokensFromContract } = useWeb3();
256
+ * const { getRewardTokens } = useTokens();
257
+ *
258
+ * const userWallet = user?.wallets?.[0]?.address;
259
+ * const rewardTokens = await getRewardTokens();
260
+ * const result = await getAccountOwnedTokensFromContract(userWallet, rewardTokens[0]);
261
+ * console.log(`User owns ${result.totalOwned} tokens`);
262
+ * ```
263
+ *
264
+ * @see {@link extractTokenIds} - Low-level helper used internally for ERC-1155
265
+ * @see {@link buildCollectionRequest} - For manual request building
266
+ */
267
+ const getAccountOwnedTokensFromContract = useCallback(async (
268
+ accountAddress: string,
269
+ token: TokenDTO,
270
+ maxTokens: number = 50
271
+ ): Promise<AccountOwnedTokensResult> => {
272
+ if (!isInitialized || !sdk) {
273
+ throw new Error('SDK not initialized. Call initialize() first.');
274
+ }
275
+
276
+ return sdk.web3.getAccountOwnedTokensFromContract(accountAddress, token, maxTokens);
277
+ }, [sdk, isInitialized]);
278
+
279
+ /**
280
+ * Build a TokenCollectionRequest from a TokenDTO.
281
+ *
282
+ * Automatically handles ERC-1155 tokenId extraction from metadata.
283
+ * Use this when you need more control over the request than
284
+ * `getAccountOwnedTokensFromContract` provides.
285
+ *
286
+ * @param accountAddress - Any valid blockchain address (wallet, contract, etc.)
287
+ * @param token - Token definition
288
+ * @param maxTokens - Maximum tokens to retrieve (default: 50)
289
+ * @returns TokenCollectionRequest ready for getTokenCollection()
290
+ */
291
+ const buildCollectionRequest = useCallback((
292
+ accountAddress: string,
293
+ token: TokenDTO,
294
+ maxTokens: number = 50
295
+ ): TokenCollectionRequest => {
296
+ if (!sdk) {
297
+ throw new Error('SDK not initialized. Call initialize() first.');
298
+ }
299
+ return sdk.web3.buildCollectionRequest(accountAddress, token, maxTokens);
300
+ }, [sdk]);
301
+
404
302
  return {
405
- // Core methods
406
303
  getTokenBalance,
407
304
  getTokenMetadata,
408
305
  getTokenCollection,
@@ -410,11 +307,10 @@ export const useWeb3 = () => {
410
307
  fetchAndProcessMetadata,
411
308
  getChainDataById,
412
309
  getExplorerUrl,
413
- // Helper methods for ERC-1155 tokens
310
+ // Helper methods
414
311
  extractTokenIds,
415
- getUserOwnedTokensFromContract,
312
+ getAccountOwnedTokensFromContract,
416
313
  buildCollectionRequest,
417
- // State
418
314
  isAvailable: isInitialized && !!sdk?.web3,
419
315
  };
420
316
  };
package/src/index.ts CHANGED
@@ -182,6 +182,7 @@ export {
182
182
  * - `useAnalytics` - Usage analytics and reporting
183
183
  * - `useFiles` - File upload and management
184
184
  * - `useWeb3` - Blockchain integration and wallet operations
185
+ * - `useEvents` - ** SDK event subscriptions for notifications**
185
186
  *
186
187
  * @example
187
188
  * **Token Operations:**
@@ -222,6 +223,26 @@ export {
222
223
  * };
223
224
  * }
224
225
  * ```
226
+ *
227
+ * @example
228
+ * **Event Subscriptions:**
229
+ * ```typescript
230
+ * import { useEvents, useEffect } from '@explorins/pers-sdk-react-native';
231
+ *
232
+ * function NotificationHandler() {
233
+ * const { subscribe, isAvailable } = useEvents();
234
+ *
235
+ * useEffect(() => {
236
+ * if (!isAvailable) return;
237
+ *
238
+ * const unsubscribe = subscribe((event) => {
239
+ * showNotification(event.userMessage, event.level);
240
+ * });
241
+ *
242
+ * return () => unsubscribe();
243
+ * }, [subscribe, isAvailable]);
244
+ * }
245
+ * ```
225
246
  */
226
247
  export {
227
248
  useAuth,
@@ -239,12 +260,16 @@ export {
239
260
  useUserStatus,
240
261
  useFiles,
241
262
  useAnalytics,
242
- useDonations
263
+ useDonations,
264
+ useEvents
243
265
  } from './hooks';
244
266
 
245
267
  // Re-export signing status types for convenience
246
268
  export type { OnStatusUpdateFn, StatusUpdateData, SigningStatusType } from './hooks';
247
269
 
270
+ // Re-export event types for convenience
271
+ export type { EventsHook, PersEvent, EventHandler, EventFilter, Unsubscribe } from './hooks';
272
+
248
273
  // ==============================================================================
249
274
  // PLATFORM ADAPTERS
250
275
  // ==============================================================================
@@ -317,4 +342,82 @@ export type {
317
342
  TokenMetadata,
318
343
  TokenCollection,
319
344
  TokenCollectionRequest
320
- } from '@explorins/pers-sdk/web3';
345
+ } from '@explorins/pers-sdk/web3';
346
+
347
+ // ==============================================================================
348
+ // TRANSACTION REQUEST FACTORY FUNCTIONS
349
+ // ==============================================================================
350
+
351
+ /**
352
+ * Transaction Request Factory Functions
353
+ *
354
+ * Factory functions for creating properly structured transaction DTOs.
355
+ * Prioritizes accountId/accountType over raw addresses for security and consistency.
356
+ *
357
+ * @example
358
+ * ```typescript
359
+ * import {
360
+ * buildMintRequest,
361
+ * buildTransferRequest,
362
+ * buildBurnRequest,
363
+ * useTransactions,
364
+ * AccountOwnerType
365
+ * } from '@explorins/pers-sdk-react-native';
366
+ *
367
+ * function TokenOperations() {
368
+ * const { createTransaction } = useTransactions();
369
+ *
370
+ * const handleMint = async () => {
371
+ * const request = buildMintRequest({
372
+ * amount: 100,
373
+ * contractAddress: '0x...',
374
+ * chainId: 137,
375
+ * recipientAccountType: AccountOwnerType.USER,
376
+ * recipientAccountId: 'user-123'
377
+ * });
378
+ * await createTransaction(request);
379
+ * };
380
+ * }
381
+ * ```
382
+ *
383
+ * @see {@link buildMintRequest} - Create mint transaction requests
384
+ * @see {@link buildBurnRequest} - Create burn transaction requests
385
+ * @see {@link buildTransferRequest} - Create transfer transaction requests
386
+ * @see {@link buildPOSTransferRequest} - Create POS transfer requests (business submits on behalf of user)
387
+ * @see {@link buildPOSBurnRequest} - Create POS burn requests (business submits burn on behalf of user)
388
+ * @see {@link buildSubmissionRequest} - Create submission requests from QR/signature data
389
+ */
390
+ export {
391
+ buildMintRequest,
392
+ buildBurnRequest,
393
+ buildTransferRequest,
394
+ buildPOSTransferRequest,
395
+ buildPOSBurnRequest,
396
+ buildSubmissionRequest
397
+ } from '@explorins/pers-sdk/transaction';
398
+
399
+ export type { POSAuthorizationOptions } from '@explorins/pers-sdk/transaction';
400
+
401
+ // ==============================================================================
402
+ // ERROR CLASSES (for instanceof checks)
403
+ // ==============================================================================
404
+
405
+ /**
406
+ * SDK Error classes for type checking in catch blocks
407
+ *
408
+ * @example
409
+ * ```typescript
410
+ * import { PersApiError } from '@explorins/pers-sdk-react-native';
411
+ *
412
+ * try {
413
+ * await sdk.campaigns.claimCampaign({ campaignId });
414
+ * } catch (error) {
415
+ * if (error instanceof PersApiError) {
416
+ * console.log(error.message); // Clean backend message
417
+ * console.log(error.code); // e.g., 'CAMPAIGN_BUSINESS_REQUIRED'
418
+ * console.log(error.status); // e.g., 400
419
+ * }
420
+ * }
421
+ * ```
422
+ */
423
+ export { PersApiError, AuthenticationError } from '@explorins/pers-sdk/core';